多线程
1.概述
进程:是一个正在执行中的程序,每个进程执行的有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中一个独立的控制单元。线程控制着进程的执行。
一个进程至少有一个线程。例如,jvm启动的时候会有一个进程java.exe。该进程至少一个线程负责java运行程序的执行,而且这个线程的运行的代码存在于main方法中,这个线程称为主线程。
拓展:实际上jvm不仅启动一个线程,还有负责垃圾回收的线程。
2.创建线程
创建线程有两种方法:
继承Thread类创建线程:
步骤:
- 定义类继承Thread。
- 复写Thread类中的run方法。
- 调用线程的start方法。(该方法两个作用:1.启动该线程。2.调用run方法。)
例:
class Demo extends Thread{
public void run{
for(int i=0;i<160;i++){
system.out.println("no");
}
}
}
class Demo{
pubilc static void main(String[] args){
Demo d=new Demo();
d.start();
for(int i=0;i<160;i++){
system.out.println("yes");
}
}
}
其实,为了更加明确是哪个线程,可以调用Thread.currentthread.getName()方法。
以上结果:yes和no交替执行。
实现Runable接口
步骤:
- 定义实现Runable接口。
- 覆盖Runable接口中的run方法。
- 通过Thread类创建对象。
- 将Runable接口的子类对象传递给Thread类的构造函数。
- 调用Thread类的start方法开启线程并调用runable接口子类的run方法。
例:多人售票:
package 平常练习;
public class Demo {
public static void main(String[] args) {
Tickets t = new Tickets();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.start();
t2.start();
t3.start();
}
}
class Tickets implements Runnable {
private int tickets = 100;
Object obj = new Object();
public void run() {
while (true) {
synchronized (obj) {
if (tickets > 0) {
try {
Thread.sleep(10);
}
catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + "sale" + tickets--);
}
}
}
}
}
上述代码出现了sleep方法和Synchronized下面将具体说明。
Synchronized
java对多线程的安全问题提供了专业的解决模式
同步代码块
Synchronized(对象){
需要同步的代码
}
对象如同锁,持有锁的线程可以在同步中执行。
没有锁的线程即使获取cpu的执行权也进不去,因为没有获取锁。
*同步的前提
1. 必须有两个或者两个以上线程。
2. 必须是多个线程使用同一个锁。
3. 必须保证同步中只能有一个线程在执行。
(好处:解决了多线程的安全问题。弊端:多线程需要判断锁,较为消耗资源。)
注意:同步函数使用的锁是this。静态方法的锁是该方法所在类的字节码文件对象,即类名.class。
提到了锁,想起前面的单例设计模式,懒汉式加锁有效解决了多线程的安全问题。
饿汉式:
class Single {
private Single(){}
private static Single s=new Single();
public static Single getInstance(){
return s;
}
}
懒汉式:
class Single{
private static volatile Single s=null;//(volatile防止指令重排序,现在我不是很懂。)
private Single();
public static Single getInstance(){
if(s==null){
synchronized (Single.class){
if(s==null)
S=new Single();
}//双重判断能有效提高效率。
}
}
return s;
}
死锁
两个或两个以上线程都在等待对方释放资源,自己才能执行,所以都会进入阻塞状态。
线程的多种状态
sleep(时间):将当前线程冻结一段时间。
wait():使当前线程冻结,通常与notify()一起使用。
notify():唤醒当前冻结的进程。
notifyAll():唤醒线程池中所有冻结的线程。
注意:后三个方法使用时必须具有锁的时候使用。也就是说,同一个锁上被等待的线程可以被同一个锁的notify唤醒,等待和唤醒必须是同一个锁。
在JDK1.5中提供了多线程升级解决方案:
- 将同步Synchronized替换成现实lock操作。
- 将Object类中的wait,notify,notifyAll,替换了condition对象。该对象可以Lock锁进行获取。
- 该示例中,实现类本方只唤醒对方的操作。
例:生产者,消费者
package 平常练习;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Demo1{
public static void main(String[] args) {
Resource r=new Resource();
producer pro=new producer(r);
consumer con=new consumer(r);
Thread t1=new Thread(pro);
Thread t2=new Thread(pro);
Thread t3=new Thread(con);
Thread t4=new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource{
private String name;
private int count=1;
private boolean flag=false;
private Lock lock=new ReentrantLock();
private Condition Condition_pro=lock.newCondition();
private Condition Condition_con=lock.newCondition();
public void set(String name)throws InterruptedException{
lock.lock();
try{
while(flag)
Condition_pro.await();
this.name=name+"=="+count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
flag=true;
Condition_con.signal();
}
finally {
lock.unlock();
}
}
public void out()throws InterruptedException{
lock.lock();
try{
while(!flag)
Condition_con.await();
System.out.println(Thread.currentThread().getName()+"..消费者.."+this.name);
flag=false;
Condition_pro.signal();
}
finally {
lock.unlock();
}
}
}
class producer implements Runnable{
private Resource res;
producer(Resource res){
this.res=res;
}
public void run() {
while(true) {
try {
res.set("+商品+");
}
catch (InterruptedException e){
}
}
}
}
class consumer implements Runnable{
private Resource res;
consumer(Resource res){
this.res=res;
}
public void run() {
while(true) {
try {
res.out();
}
catch (InterruptedException e){
}
}
}
}
停止线程
stop方法已经过时,如何停止线程?
只有一种——run方法结束。开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:当线程处于了冻结状态,就不会读取到标记,那么线程就不会结束,当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,这样就可以操作标记让线程结束。thread类提供了该方法——interrupt();
例:
package 平常练习;
public class StopThreadDemo {
public static void main(String[] args) {
StopThread st=new StopThread();
Thread t1=new Thread(st);
Thread t2=new Thread(st);
t1.start();
t2.start();
int num=0;
while(true){
if(num++==60){
//st.changeFlag();
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+"..."+num);
}
System.out.println("over");
}
}
class StopThread implements Runnable{
private boolean flag=true;
public synchronized void run()
{
while(flag){
try{
wait();
}
catch(InterruptedException e){
System.out.println(Thread.currentThread().getName()+"...Exception");
}
System.out.println(Thread.currentThread().getName()+"....run");
}
}
public void changeFlag(){
flag=false;
}
}
守护线程
线程.setDaemon(boolean),当bollean为true时,该线程为守护线程,注意该方法必须在线程开启前调用。
守护线程的特点:当正在运行的线程都是守护线程时,java虚拟机退出。
join方法
当A线程执行了B线程的join方法时,A就会等待,等B线程都执行完,A才会执行,join可以用来加入线程执行。
yeild和设置线程优先级
.yeild方法:可以临时是放线程执行权,,使多个线程平均一下输出频率,交替更换线程。
设置优先级:.setPriority(Thread.MAX-PRIORITY);设为最大优先级,获得最大频率。