------
Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
多线程的描述
一.进程、线程
(1)进程:一个正在执行的程序。每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
(2)线程:进程中一个负责程序的控制单元。线程在控制着进程的执行。只要进程中有一个线程在执行,进程就不会结束。
二.多线程
(1)多线程:在java虚拟机启动的时候会有一个java.exe的执行程序,也就是一个进程。该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。JVM启动除了执行一个主线程,还有负责垃圾回收机制的线程。像种在一个进程中有多个线程执行的方式,就叫做多线程。
(2)多线程的好处:解决了多部分代码同时运行的问题,提高程序执行效率。
多线程的创建
一.继承方式(继承Thread)
(1)步骤:1. 定义一个类继承Thread类。
2. 覆盖Thread类中的run方法。
3. 直接创建Thread的子类对象创建线程。
4. 调用start方法开启线程并调用线程的任务run方法执行。
2. 覆盖Thread类中的run方法。
3. 直接创建Thread的子类对象创建线程。
4. 调用start方法开启线程并调用线程的任务run方法执行。
(2)分析:1. Thread类中的run方法,用于存储线程要运行的代码,所以自定义线程就必须复写run方法。
2.如果线程直接调用run方法,等同于只有一个线程(main)在执行。所以必须要调用start方法才能启动线程。
(3)例子:
/**
* 创建两个线程,并输出结果
* @author Administrator
*/
class Test extends Thread//继承thread类
{
String name;
Test(String name)
{
this.name=name;
}
public void run()//复写run方法
{
for(int x=0;x<60;x++)
{
System.out.println(name+"x="+x);
}
}
}
public class ThreadTest {
public static void main(String[] args)
{
Test t1=new Test("线程1---");//创建线程
Test t2=new Test("线程2------");
t1.start();//启动线程
t2.start();
for(int x=0;x<60;x++)//main线程
{
System.out.println("main线程-"+"x="+x);
}
}
}
输出结果为:
通过结果可看出:执行是随机、交替执行的。
二.实现方式(实现Runnable)
(1)步骤: 1. 定义类实现Runnable接口。
2. 覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3. 通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
2. 覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3. 通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
4. 调用线程对象的start方法开启线程。
(2)分析: 1. 为什么要将Runnable接口的子类对象传递给Thread的构造函数?因为线程的任务都封装在Runnable接口子类对象
的run方法中。
所以要在线程对象创建时就必须明确要运行的任务。
2. 实现的方式避免了Java单继承的局限性。所以,创建线程时用实现的方式较为常见。
(3)例子:
/**
* 创建两个线程,并输出结果
* @author Administrator
*/
class Test implements Runnable//实现Runnable接口
{
public void run()//复写run方法
{
for(int x=0;x<60;x++)
{
System.out.println(Thread.currentThread().getName() + "-----" + x);
}
}
}
public class ThreadTest {
public static void main(String[] args)
{
Test t=new Test();//创建Runnable子类对象
Thread d1=new Thread(t);//创建线程并把Runnable子类对象作为实际参数传给Thread的构造函数
Thread d2=new Thread(t);
d1.start();//启动线程
d2.start();
for(int x=0;x<60;x++)//main线程
{
System.out.println("main线程-"+"x="+x);
}
}
输出结果为:
线程的安全问题
一.安全问题出现的原因
(1)多个线程在操作共享的数据。
(2)操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。
(2)操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。
二.解决办法——同步
(1)同步代码块:格式 synchronized(对象){ 需要被同步的代码;}。其中的对象可以new一个Object对象传进去。
(2)同步函数:格式:在函数上加上synchronized修饰符即可。
(3)同步的前提:1.必须要有两个或者两个以上的线程。2.必须是多个线程使用同一个锁。
(4)同步的利弊:利:解决了多线程的安全问题。弊:多个线程需要判断锁,较为消耗资源。
(5)同步要注意的问题:1.明确哪些代码是多线程运行代码。
2.明确共享数据。
3. 明确多线程运行代码中哪些语句是操作共享数据的。
(6)多线程下的单例模式:
class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s ==null)
{
synchronized(Single.class)
{
if(s == null)
s = new Single();
}
}
return s ;
}
}
死锁
一.死锁的出现
当同步中嵌套同步时,就有可能出现死锁现象。
二.死锁示例
/**
写一个死锁程序
*/
class LockTest implements Runnable//定义一个类来实现Runnable,并复写run方法
{
static Object locka = new Object();
static Object lockb = new Object();
private boolean flag;
LockTest(boolean flag)
{
this.flag=flag;
}
public void run()
{
if(flag)
{
while(true)
{
synchronized(locka)//a锁
{
System.out.println("------if_locka");
synchronized(lockb)//b锁
{
System.out.println("------if_lockb");
}
}
}
}
else
{
while(true)
{
synchronized(lockb)//b锁
{
System.out.println("------else_lockb");
synchronized(locka)//a锁
{
System.out.println("------else_locka");
}
}
}
}
}
}
public class DeadLock
{
public static void main(String[] args)
{
new Thread(new LockTest(true)).start();
new Thread(new LockTest(false)).start();
}
}
线程间通信
一.多个线程在处理统一资源,但是任务却不同,这时候就需要线程间通信。
二.等待/唤醒机制涉及的方法:
(1) wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。
(2) notify():唤醒线程池中的一个线程(任何一个都有可能)。
(3) notifyAll():唤醒线程池中的所有线程。
(2) notify():唤醒线程池中的一个线程(任何一个都有可能)。
(3) notifyAll():唤醒线程池中的所有线程。
三.wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中
(1)
这些方法存在与同步中。
(2)使用这些方法时必须要标识所属的同步的锁。同一个锁上wait的线程,只可以被同一个锁上的notify唤醒。
(3)锁可以是任意对象,所以任意对象调用的方法一定定义object类中。
四.wait(),sleep()有什么区别
(1) wait:释放执行权,释放锁。
(2) sleep:释放执行权,不释放锁。
线程的停止
一.让run方法结束
(1)
开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让
run
方法结束,也就是线程结束。
(2)run方法中有如下代码,设置一个flag标记。
public void run()
{
while(flag)
{
System.out.println(Thread.currentThread().getName()+"....run");
}
}
二.线程处于冻结状态
(1)线程处于了冻结状态,无法读取标记。
(2)Thread类提供该方法interrupt()强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。
(3)例子:
class StopThread implements Runnable
{
private boolean flag =true;
public void run()
{
while(flag)
{
System.out.println(Thread.currentThread().getName()+"....run");
}
}
public void changeFlag()
{
flag = false;
}
}
class StopThreadDemo
{
public static void main(String[] args)
{
StopThread st = new StopThread();
<span style="white-space:pre"> </span>Thread t1 = new Thread(st);
<span style="white-space:pre"> </span>Thread t2 = new Thread(st);
<span style="white-space:pre"> </span>t1.start();
<span style="white-space:pre"> </span>t2.start();
int num = 0;
while(true)
{
if(num++ == 60)
{
t1.interrupt();//清除冻结状态
t2.interrupt();
st.changeFlag();//改变循环标记
break;
}
System.out.println(Thread.currentThread().getName()+"......."+num);
}
System.out.println("over");
}
}