------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
多线程总结
一、多线程概述
进程:进程是指正在运行中的程序,当打开系统的资源管理器时,会发现很多进程,这些都是在计算机中正在运行中的程序。进程是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。
线程:线程是指进程中的一条执行路径
a.单线程:如果一个进程只有一条执行路径,那么这个程序就会沿着这条执行路径,顺序执行,知道结束,这就是单线程程序。
b.多线程:如果一个进程有多条执行路径,那么程序会在这多条执行路径之间并行执行。
1、多进程和多线程的意义
a:多进程的意义:提高CPU的使用率.
b:多线程的存在是:提供了应用程序的使用率.
2、JVM运行原理以及JVM启动的线程探讨
JVM的运行原理::
我们在使用java命令在运行一个程序的时候,其实是启动了JVM,而JVM的启动相当于一个进程.而同时这个进程会启动一个线程,通过这
个线程来调用main方法,而这个线程就是我们的主线程.
JVM的启动是多线程的吗?
是多线程的.因为JVM至少启动了两个线程,一个是主线程 , 一个是垃圾回收线程.
3、实现多线程的两种方式
第一种方式的步骤:
a: 创建一个类,然后让这个类去继承Thread类
b: 复写run方法(run方法中封装的都是要被线程执行的代码, run方法中代码的特点: 比较耗时的代码)
c: 创建a中定义的类的对象
d: 启动线程(启动线程使用的是start()方法)
public class MyThread extends Thread {
@Override
public void run() {
for(int x = 0 ; x < 100 ; x++){
System.out.println(x);
}
}
}
public class SellTicketsDemo {
public static void main(String[] args) {
// 创建SellTickets对象
SellTickets st = new SellTickets() ;
// 创建Thread对象
Thread t1 = new Thread(st , "窗口1") ;
Thread t2 = new Thread(st , "窗口2") ;
Thread t3 = new Thread(st , "窗口3") ;
// 启动线程
t1.start() ;
t2.start() ;
t3.start() ;
}
}
第二种方式的步骤:
a: 创建一个类,然后让这个类去实现Runnable接口
b: 复写run方法
c: 创建a中定义的类的对象
d: 创建Thread类的对象,然后把c中的对象作为参数传递
e: 启动线程
public class MyThread implements Runnable {
@Override
public void run() {
for(int x = 0 ; x < 100 ; x++){
System.out.println(Thread.currentThread().getName() + "---" + x);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
// 创建MyThread类的对象
MyThread my = new MyThread() ;
// 创建Thread类的对象,把my中的对象作为参数传递
// Thread t1 = new Thread(my) ;
// Thread t2 = new Thread(my) ;
/**
* public Thread(Runnable target, String name)分配新的 Thread 对象。可以给线程设置名称
*/
Thread t1 = new Thread(my , "张三") ;
Thread t2 = new Thread(my , "李四") ;
// 设置名称
// t1.setName("张三") ;
// t2.setName("李四") ;
// 启动线程
t1.start() ;
t2.start() ;
}
}
4、线程调度
调度模型:
a: 分时调度:就是给每一个线程分配指定的时间进行执行
b: 抢占式调度:优先的执行优先级高的线程,如果线程的优先级相同,那么就随机执行一个.每一个线程应该都存在一个默认的优先级.
而java语言采用的就是抢占式调度
如何获取线程的优先级?
public final int getPriority(): 获取线程的优先级(线程的默认优先级是: 5)
public final void setPriority(int newPriority): 给线程设置优先级(存在一个范围这个范围是1-10)
public class ThreadDemo {
public static void main(String[] args) {
// 创建线程对象
MyThread t1 = new MyThread() ;
MyThread t2 = new MyThread() ;
// 给线程设置名称
t1.setName("小菲菲") ;
t2.setName("公孙瓒") ;
// 获取线程的优先级
/**
* public final int getPriority()返回线程的优先级。
*/
/**
* 给线程设置优先级: public final void setPriority(int newPriority)更改线程的优先级。
*/
// java.lang.IllegalArgumentException: 非法的参数异常
// t1.setPriority(10000) ;
/**
* 线程的优先级是有一个范围的这个范围在[1,10]
*/
t1.setPriority(10) ;
// System.out.println(t1.getPriority()) ; // 线程的默认优先级是5
// System.out.println(t2.getPriority()) ;
// 启动线程
t1.start() ;
t2.start() ;
}
}
5、线程获取名称
如何获取线程的名称? public final String getName()
如何给线程设置名称? public final void setName(String name), 也可以通过构造方法进行设置
如何获取主线程的名称? 获取到当前正在执行的线程,然后在获取线程名称.如何获取到当前正在执行的线程.
在Thread类中存在一个静态的方法:
public static Thread currentThread():获取当前正在执行的线程
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
// 创建线程对象
MyThread t1 = new MyThread() ;
MyThread t2 = new MyThread() ;
MyThread t3 = new MyThread() ;
// 给线程设置名称
t1.setName("李渊") ;
t2.setName("李元吉") ;
t3.setName("李元霸") ;
// 启动线程
t1.start() ;
/**
* 加入李源线程
*/
t1.join() ;
t2.start() ;
t3.start() ;
}
}
public class MyThread extends Thread {
@Override
public void run() {
for(int x = 0 ; x < 100 ; x++) {
System.out.println(getName() + "----" + x);
}
}
}
6、线程控制
(1)线程休眠
public static void sleep(long time) ;让线程休眠一段时间
public class MyThread extends Thread {
@Override
public void run() {
for(int x = 0 ; x < 100 ; x++) {
// 线程休眠
try {
Thread.sleep(2000) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + "----" + x);
}
}
}
/**
* 线程控制之线程休眠:
* public static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),
*/
public class ThreadDemo {
public static void main(String[] args) {
// 创建线程对象
MyThread t1 = new MyThread() ;
// 给线程设置名称
t1.setName("张三") ;
// 启动线程
t1.start() ;
}
}
(2)线程加入
public final void join();等待该线程终止. 等待该线程执行完毕以后在执行其他的线程
/**
* 线程加入: public final void join() 等待该线程终止。
* 等待该线程执行完毕以后在执行其他的线程
* 线程加入: 在启动线程以后在进行加入,如果在线程启动以前在进行加入,则无效.
*
*/
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
// 创建线程对象
MyThread t1 = new MyThread() ;
MyThread t2 = new MyThread() ;
MyThread t3 = new MyThread() ;
// 给线程设置名称
t1.setName("李渊") ;
t2.setName("李元吉") ;
t3.setName("李元霸") ;
// 启动线程
t1.start() ;
/**
* 加入李源线程
*/
t1.join() ;
t2.start() ;
t3.start() ;
}
}
public class MyThread extends Thread {
@Override
public void run() {
for(int x = 0 ; x < 100 ; x++) {
System.out.println(getName() + "----" + x);
}
}
}
(3)线程礼让
public static void yield():暂停当前正在执行的线程对象,并执行其他线程
/**
* 线程礼让:
* public static void yield()暂停当前正在执行的线程对象,并执行其他线程。
* 这线程礼让是暂停当前正在执行的线程,而这个线程的暂停时间很短很短,那么如果在该线程暂停完毕以后其他的线程还没有在
* 抢到CPU的执行权,那么该线程就会继续和其他的线程抢占CPU的执行权.
*/
public class ThreadDemo {
public static void main(String[] args) {
// 创建线程对象
MyThread t1 = new MyThread() ;
MyThread t2 = new MyThread() ;
// 给线程设置名称
t1.setName("刘亦菲") ;
t2.setName("惠雷雨") ;
// 启动线程
t1.start() ;
t2.start() ;
}
}
public class MyThread extends Thread {
@Override
public void run() {
for(int x = 0 ; x < 100 ; x++) {
System.out.println(getName() + "----" + x);
// 线程礼让
Thread.yield() ;
}
}
}
(4)线程守护
public final void setDaemon(boolean on):把指定的线程设置成一个守护线程.当执行的线程都是守护线程的时候,JVM停止运行
**
* 线程控制之线程守护:
* public final void setDaemon(boolean on): 把指定的线程设置成守护线程
*/
public class ThreadDemo {
public static void main(String[] args) {
// 创建线程
MyThread t1 = new MyThread() ;
MyThread t2 = new MyThread() ;
// 给线程设置名称
t1.setName("关羽") ;
t2.setName("张飞") ;
// 设置关羽和张飞线程为守护线程
t1.setDaemon(true) ;
t2.setDaemon(true) ;
// 启动线程
t1.start() ;
t2.start() ;
// 获取主线程,给主线程设置名称
Thread.currentThread().setName("刘备") ;
for(int x = 0 ; x < 10 ; x++){
System.out.println(Thread.currentThread().getName() + "---" + x);
}
}
}
(5)线程中断
public final void stop():停止线程的运行
public void interrupt():打断阻塞状态. 继续执行下面的代码,但是该方法会抛出异常.
/**
* 线程控制之线程中断:
* public final void stop(): 中断线程
* public void interrupt(): 中断线程。
* 如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,
* 或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,
* 则其中断状态将被清除,它还将收到一个 InterruptedException。
*/
public class ThreadDemo {
public static void main(String[] args) {
// 创建线程对象
MyThread t1 = new MyThread() ;
// 设置线程名称
t1.setName("宋承宪") ;
// 启动线程
t1.start() ;
try {
Thread.sleep(3000) ;
// 中断线程t1
// t1.stop() ;
t1.interrupt() ;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
6、线程获安全
可以使用同步代码块:
格式:
synchronized(对象){
要被同步的代码 ;
}
同步代码块在保证数据的安全性主要依赖这个对象,要求使用的对象必须是同一个.这个对象可以看做成一把锁.
同步代码块的好处: 可以保证数据的安全性
同步代码块的弊端: 效率降低了
同步代码块以及同步方法和静态同步方法的锁对象的问题:
同步代码块的锁对象: 是任意的对象
同步方法的锁对象: 是this
静态同步方法的锁对象:是该类对应的字节码文件对象.
例1:同步代码块
public class SellTickets implements Runnable {
// 定义票的数量
private static int num = 100 ;
private static final Object obj = new Object() ;
@Override
public void run() {
while(true) {
synchronized(obj) {
if(num > 0) {
try {
Thread.sleep(100) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (num--) + "张票");
}
}
}
}
}
public class SellTicketsDemo {
public static void main(String[] args) {
// 创建SellTickets对象
SellTickets st = new SellTickets() ;
// 创建Thread对象
Thread t1 = new Thread(st , "窗口1") ;
Thread t2 = new Thread(st , "窗口2") ;
Thread t3 = new Thread(st , "窗口3") ;
// 启动线程
t1.start() ;
t2.start() ;
t3.start() ;
}
}
例2:同步方法和静态代码块
public class SellTickets implements Runnable {
// 定义票的数量
private static int num = 100 ;
private static final Object obj = new Object() ;
private int n = 0 ;
@Override
public void run() {
while(true) {
if(n % 2 == 0){
synchronized(SellTickets.class) {
if(num > 0) {
try {
Thread.sleep(100) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (num--) + "张票");
}
}
}else {
sellTickets() ;
}
n++ ;
}
}
/**
* 现在的这个方法的方法体添加了同步代码块,那么我们可不可以把这个synchronized关键字定义在方法上呢?
*/
// public void sellTickets() {
//
// synchronized(obj) {
//
// if(num > 0) {
//
// try {
// Thread.sleep(100) ;
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// System.out.println(Thread.currentThread().getName() + "正在出售第" + (num--) + "张票");
// }
//
// }
// }
// 同步方法
// public synchronized void sellTickets() {
//
// if(num > 0) {
//
// try {
// Thread.sleep(100) ;
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// System.out.println(Thread.currentThread().getName() + "正在出售第" + (num--) + "张票");
// }
// }
// 静态同步方法
public static synchronized void sellTickets() {
if(num > 0) {
try {
Thread.sleep(100) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (num--) + "张票");
}
}
}
/**
* 同步代码块的锁对象: 可以是任意的对象
* 同步方法的锁对象: 是this
* 静态同步方法的锁对象: 是该类对应的字节码文件对象
*/
public class SellTicketsDemo {
public static void main(String[] args) {
// 创建SellTickets对象
SellTickets st = new SellTickets() ;
// 创建Thread对象
Thread t1 = new Thread(st , "窗口1") ;
Thread t2 = new Thread(st , "窗口2") ;
Thread t3 = new Thread(st , "窗口3") ;
// 启动线程
t1.start() ;
t2.start() ;
t3.start() ;
}
}
7、死锁
/*
写一个死锁程序
*/
//定义一个类来实现Runnable,并复写run方法
class LockTest implements Runnable
{
private boolean flag;
LockTest(boolean flag)
{
this.flag=flag;
}
public void run()
{
if(flag)
{
while(true)
{
synchronized(LockClass.locka)//a锁
{
System.out.println(Thread.currentThread().getName()+"------if_locka");
synchronized(LockClass.lockb)//b锁
{
System.out.println(Thread.currentThread().getName()+"------if_lockb");
}
}
}
}
else
{
while(true)
{
synchronized(LockClass.lockb)//b锁
{
System.out.println(Thread.currentThread().getName()+"------else_lockb");
synchronized(LockClass.locka)//a锁
{
System.out.println(Thread.currentThread().getName()+"------else_locka");
}
}
}
}
}
}
//定义两个锁
class LockClass
{
static Object locka = new Object();
static Object lockb = new Object();
}
class DeadLock
{
public static void main(String[] args)
{
//创建2个进程,并启动
new Thread(new LockTest(true)).start();
new Thread(new LockTest(false)).start();
}
}
8、线程间通信
其实就是多个线程在操作同一个资源,但是操作的动作不同。
/*
有一个资源
一个线程往里存东西,如果里边没有的话
一个线程往里取东西,如果里面有得话
*/
//资源
class Resource
{
private String name;
private String sex;
private boolean flag=false;
public synchronized void setInput(String name,String sex)
{
if(flag)
{
try{wait();}catch(Exception e){}//如果有资源时,等待资源取出
}
this.name=name;
this.sex=sex;
flag=true;//表示有资源
notify();//唤醒等待
}
public synchronized void getOutput()
{
if(!flag)
{
try{wait();}catch(Exception e){}//如果木有资源,等待存入资源
}
System.out.println("name="+name+"---sex="+sex);//这里用打印表示取出
flag=false;//资源已取出
notify();//唤醒等待
}
}
//存线程
class Input implements Runnable
{
private Resource r;
Input(Resource r)
{
this.r=r;
}
public void run()//复写run方法
{
int x=0;
while(true)
{
if(x==0)//交替打印张三和王羲之
{
r.setInput("张三",".....man");
}
else
{
r.setInput("王羲之","..woman");
}
x=(x+1)%2;//控制交替打印
}
}
}
//取线程
class Output implements Runnable
{
private Resource r;
Output(Resource r)
{
this.r=r;
}
public void run()//复写run方法
{
while(true)
{
r.getOutput();
}
}
}
class ResourceDemo2
{
public static void main(String[] args)
{
Resource r = new Resource();//表示操作的是同一个资源
new Thread(new Input(r)).start();//开启存线程
new Thread(new Output(r)).start();//开启取线程
}
}
本文详细介绍了Java多线程的基础概念与核心技术,包括进程与线程的区别、JVM启动线程机制、实现多线程的两种方式、线程调度模型、线程控制方法如休眠、加入、礼让等,以及同步机制防止数据不一致问题。

337

被折叠的 条评论
为什么被折叠?



