JAVA多线程

一: java多线程的四种实现方式

注:创建线程的对象只代表该线程,并不是线程本身。线程本身是一条运行的子程序

1:通过继承Thread类,重写run()方法

        继承Thread类,实现一个自定义的线程类,重写类中的run()方法,通过调用自定义线程类的构造方法创建一个线程,通过调用自定义线程类的start()方法,启动一个线程。启动后,线程自动调用run()方法运行线程。

package hhxy;

class MyThread extends Thread {
	private int sum;

	public MyThread(int sum, String name) {
		super(name);
		this.sum = sum;
	}

	@Override
	public void run() {
		while (sum > 0)
			System.out.println(Thread.currentThread().getName() + "卖出编号:" + (sum--) + "的票");
	}
}

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		MyThread my = new MyThread(20, "lwk");
		my.start();
	}

}

2:实现Runnable接口,实现run()方法

       新建一个自定义类实现Runable接口,实现Runnable接口中的run()方法。创建线程时,先创建一个自定义的类的对象 ,创建一个Thread类的对象,将自定义类的对象作为参数传入Thread类的构造方法中。通过调用 Thread对象的 start()方法启动线程,自动调用Thread类对象的run()方法运行线程。Thread()类对象的run()方法自动调用自定义类run() 方法。

package hhxy;

class MyRunnable implements Runnable {
	private int sum;

	public MyRunnable(int sum) {
		this.sum = sum;
	}

	@Override
	public void run() {
		while (sum > 0)
			System.out.println(Thread.currentThread().getName() + "卖出编号:" + (sum--) + "的票");
	}
}

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		MyRunnable myable = new MyRunnable(20);
		Thread my = new Thread(myable, "lwk");
		my.start();
	}

}

3:实现Callable接口,实现call()方法

(1) :Callable封装一个异步运行的任务,Callable接口与Runable接口类似,但是Callable有返回值,Callable是一个参数化的类型,只有一个方法call(),类型参数是返回值的类型。例如Callable<Integer>返回一个Integer对象。

(2):Future保存异步运算的结果可以启动一个计算,将Future对象交给某个线程,然后忘掉它,Future对象的所有者在结果计算好以后就可以获得它。

(3):FutureTask包装器是个非常便利的机制,可将Callable转换成Future和Runable,它同时实现这两个接口

(4):创建线程时,先创建一个实现Callable接口的自定义类的对象,然后将此对象封装到FutureTask中,FutureTask便能作为Runable和Future使用。启动线程时仍然调用start()方法。

package hhxy;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyCallable implements Callable<Integer> {
	private int sum;

	public MyCallable(int sum) {
		this.sum = sum;
	}

	@Override
	public Integer call() throws Exception {
		// TODO Auto-generated method stub
		Integer add = 0;
		for (int i = 1; i <= sum; i++) {
			add = i + add;
		}
		return add;
	}
}

public class Main {

	public static void main(String[] args) throws InterruptedException, ExecutionException {
		// TODO Auto-generated method stub
		MyCallable mycall = new MyCallable(20);
		FutureTask<Integer> task = new FutureTask<Integer>(mycall);
		Thread my = new Thread(task);
		my.start();
		Integer result = task.get();
		System.out.println(result);
	}

}

4:利用线程池是创建多线程 

(1):创建线程是需要代价的,如果每次创建的都是生存周期很短的线程,那太浪费资源了,此时便需要使用线程池,一个线程池中包含许多准备运行的空闲线程,将Runnable对象交给线程池,就会有一个线程调用run()方法。当run()方法退出时线程不会死亡,而是在池中准备为下一个请求服务。

(2):执行器(Executor)类有很多静态方法来构建线程池。

方法描述
newCachedThreadPool()必要时创建新线程,空闲线程会被保留60秒
newFixedThreadPool(int n)该池包含固定数量n的线程,空闲线程会一直被保留
newSingleThreadExecutor()只有一个线程,该线程顺序执行每一个提交的任务
newScheduledThreadPool()用于预定执行而构建的固定线程池,替代java.util.Timer
newSingleThreadScheduledEcecutor()用于预定执行而构建的单线程"池”

(3):前三种方法返回了实现ExecutorService接口的ThreaPoolExecutor类的对象。可用下面的方法将一个Runnable对象,或者Callable对象题提交给ExecutorService

方法描述
Future<?> submit(Runnable task)返回一个Future<?>对象,使用这个对象调用Future类的方法,但是调用get()时返回null
Future<T> submit(Runnable task,result)返回的Future<T>对象,调用get()时返回result
Future<T> submit(Callable<T> task)

传入Callable<T>,返回的Future将在计算结果完成后得到它

package hhxy;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

class MyCallable implements Callable<Integer>{
	private int sum;
	public MyCallable(int sum)
	{
		this.sum=sum;
	}
	public Integer call() throws Exception {
		// TODO Auto-generated method stub
		Integer add=0;
		for(int i=1;i<=sum;i++)
		{
			add=i+add;
		}
		return add;
	}
}
public class Main {

	public static void main(String[] args) throws InterruptedException, ExecutionException {
		ExecutorService es= Executors.newFixedThreadPool(10);
		MyCallable myCall=new MyCallable(20);
		Future<Integer> future=es.submit(myCall);
		System.out.println(future.get());
	}

}

二:java的两种线程锁

        线程锁的存在是为了线程安全,当多个线程访问一块共享资源时,就会因为线程之间无序的随意访问造成无法预估的错误,因此引入了线程锁,即对象中的被上锁一块资源只能被拿到锁的线程访问,即一次只能被一个线程访问。当上一个线程访问结束后,释放锁,下一个线程才能重新拿到锁访问该资源。以下代码便是线程不安全的

package hhxy;
class  MyRunnable implements Runnable{
	private int sum;
	public MyRunnable(int sum)
	{
		this.sum=sum;
	}
	@Override
	public void run() {
		while(sum>0)
		{
			try {
				Thread.sleep(500); //通过延时放大错误
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"卖掉编号:"+(sum--)+"的票");
		}
	}
}
public class Main {
	public static void main(String[] args) {
		MyRunnable myRun=new MyRunnable(20); 
		Thread lwk = new Thread(myRun,"lwk");
		Thread lz=new Thread(myRun,"lz"); 
		lwk.start();
		lz.start();
	}
}

    

可以看到有些编号的票被重复卖出,而且次序混乱,因此线程不安全,故引出了锁机制

1:内部对象锁(synchronized关键字)

        每一个对象都有一个内部锁,被关键字synchronized声名的方法或者代码块如果被一个线程访问,那么该线程必须要获得对象的内部锁,synchronized的声名有以下两种方式

(1):声名方法:public synchronized void method( ...)

(2):声名代码块 synchronized (this|Object|Class.class)

package hhxy;

import java.io.SyncFailedException;

class  MyRunnable implements Runnable{
	private int sum;
	public MyRunnable(int sum)
	{
		this.sum=sum;
	}
	@Override
	public void run() {
		while(sum>0)
		{
			
			synchronized (this) {
				try {
				Thread.sleep(500); //通过延时放大错误
			    } catch (InterruptedException e) {
				   e.printStackTrace();
			    }
				if(sum<=0)
					break;
			    System.out.println(Thread.currentThread().getName()+"卖掉编号:"+(sum--)+"的票");
			}
			
		}
	}
}
public class Main {
	public static void main(String[] args) {
		MyRunnable myRun=new MyRunnable(20); 
		Thread lwk = new Thread(myRun,"lwk");
		Thread lz=new Thread(myRun,"lz"); 
		lwk.start();
		lz.start();
	}
}

   

由图可知,synchronized 关键字声明后线程安全

2:锁对象(ReentrantLock)

(1):锁对象的应用

java SE 5.0以后引入了ReentrantLock类,该类的对象提供一个显式锁,基本结构如下:

try{

     myLock.lock();

     .......

}

finally{

    unLock();

}

当该锁对象调用lock()方法时,代码块被上锁,当前线程取得锁,其他线程因没有锁,因此无法通过lock(),直到该锁对象调用unLock()方法,当前线程才会释放锁。因此必须要手动释放锁。

package hhxy;

import java.io.SyncFailedException;
import java.util.concurrent.locks.ReentrantLock;

class  MyRunnable implements Runnable{
	private int sum;
	private ReentrantLock myLock;
	public MyRunnable(int sum)
	{
		this.sum=sum;
		myLock=new ReentrantLock();
	}
	@Override
	public void run() {
		while(sum>0)
		{	
			try {
				Thread.sleep(500); //延时放大错误
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			myLock.lock();
			try {
				if(sum<=0)
					break;
			    System.out.println(Thread.currentThread().getName()+"卖掉编号:"+(sum--)+"的票");
			}
			finally {
				myLock.unlock();
			}
			
		}
	}
}
public class Main {
	public static void main(String[] args) {
		MyRunnable myRun=new MyRunnable(20); 
		Thread lwk = new Thread(myRun,"lwk");
		Thread lz=new Thread(myRun,"lz"); 
		lwk.start();
		lz.start();
	}
}

运行结果与内部对象锁(synchronized关键字)相似,不再贴出运行结果

2:条件对象(Condition)

       当线程进入临界区(被锁的资源)却发现只有符合某条件才能继续运行,此时便需要一个条件对象来管理这些线程。一个锁对象可以有一个或多个条件对象,可以用一个newCondition()方法获得条件对象。

条件对象的方法:

方法描述
await()将此线程放入条件对象的等待集中
signalAll()解除该条件对象等待集中的所有线程的阻塞状态
signal()从条件对象的等待集的线程中随机选择一个线程,解除其阻塞状态

     

package hhxy;

import java.io.SyncFailedException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

class  MyRunnable implements Runnable{
	private int sum;
	private ReentrantLock myLock;
	private Condition con;
	public MyRunnable(int sum)
	{
		this.sum=sum;
		myLock=new ReentrantLock();
		con=myLock.newCondition();
	}
	@Override
	public void run() {
		while(sum>0)
		{	
			try {
				Thread.sleep(500); //延时放大错误
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			myLock.lock();
			try {
				if(sum<=0)
					break;
				if(sum%4==0&&Thread.currentThread().getName().equals("lwk"))
				{
					con.await();
				}
			    System.out.println(Thread.currentThread().getName()+"卖掉编号:"+(sum--)+"的票");
			    con.signalAll();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			finally {
				myLock.unlock();
			}
			
		}
	}
}
public class Main {
	public static void main(String[] args) {
		MyRunnable myRun=new MyRunnable(20); 
		Thread lwk = new Thread(myRun,"lwk");
		Thread lz=new Thread(myRun,"lz"); 
		lwk.start();
		lz.start();
	}
}

三:线程的五种状态

1:新建状态

线程刚刚被创建

2:可运行状态

调用start方法,但还未运行run()方法

3:运行状态

运行run()方法

4:阻塞状态

(1)同步阻塞:当线程访问一个共享资源时,该对象的锁被另一个线程获取而还未释放,此时该线程将进入该对象的锁池中等待锁被释放,此时造成同步阻塞

(2)等待阻塞:当一个线程中某对象调用wait()方法时,该线程进入到该对象的等待池中,等待另一个访问该对象的线程调用notify()方法或者notifyAll()重新唤醒,此时造成等待阻塞,如不能及时唤醒,可能照成死锁。

(3)其他阻塞:当线程调用sleep()方法或者join()方法时

5:死亡状态

run()方法正常退出,或异常中断,线程死亡

四:一些易混方法

1:sleep(int n)/yield()

       两个方法都是静态方法,在哪个线程中调用,作用于哪个线程。sleep(int n)方法使线程休眠n毫秒,线程不释放当前对象的锁,造成阻塞。yield()方法使线程暂停一下,放弃CPU资源但是不释放锁,yield()方法将执行机会让给高于或等于它优先级的线程,如没有符合条件的线程,则该线程继续执行

package hhxy;
public class Main {
	public static void main(String[] args) throws InterruptedException {
		Thread.sleep(500);
		Thread.yield();
		System.out.println("hello");
	}
}

2:wait()/notify()/notifyAll()和await()/signal()/signalAll()

(1):一个对象调用wait()方法,访问当前对象的线程被放入该对象的等待池中,线程释放锁,等待下一个访问该对象的线程调用notify()/notifyAll()方法,将该线程从等待池中唤醒。

package hhxy;

import java.io.SyncFailedException;
class Sale{
	private int sum=20;
	public void sale() throws InterruptedException
	{
		while(sum>0)
		{
			synchronized (this) {
				if(sum<=0)
					break;
				if(sum%4==0&&Thread.currentThread().getName().equals("lwk"))
					wait();
				Thread.sleep(500);
				System.out.println(Thread.currentThread().getName()+"卖出编号:"+(sum--)+"的票");
				this.notifyAll();
			}
		}
	}
}
class  MyRunnable implements Runnable{
	private Sale sale=new Sale();
	public void run() {
		try {
			sale.sale();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
public class Main {
	public static void main(String[] args) {
		MyRunnable myRun=new MyRunnable(); 
		Thread lwk = new Thread(myRun,"lwk");
		Thread lz=new Thread(myRun,"lz"); 
		lwk.start();
		lz.start();
	}
}

(2):一个条件对象调用await()方法,访问当前条件对象的线程被放入该条件对象的等待池中,线程释放锁,等待下一个线程调用同一条件对象notify()/notifyAll()方法,将该线程从等待池中唤醒。

package hhxy;

import java.io.SyncFailedException;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
class Sale{
	private int sum=20;
	private Condition con;
	private ReentrantLock lock =new ReentrantLock();
	public void sale() throws InterruptedException
	{
		while(sum>0)
		{
			try {
				lock.lock();
				con=lock.newCondition();
				if(sum<=0)
					break;
				if(sum%4==1&&Thread.currentThread().getName().equals("lwk"))
					con.await();
				Thread.sleep(500);
				System.out.println(Thread.currentThread().getName()+"卖出编号:"+(sum--)+"的票");
				con.signalAll();
			}
			finally {
				lock.unlock();
			}
		}
	}
}
class  MyRunnable implements Runnable{
	private Sale sale=new Sale();
	public void run() {
		try {
			sale.sale();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
public class Main {
	public static void main(String[] args) {
		MyRunnable myRun=new MyRunnable(); 
		Thread lwk = new Thread(myRun,"lwk");
		Thread lz=new Thread(myRun,"lz"); 
		lwk.start();
		lz.start();
	}
}

运行结果与wait()/notify()/notifyAll()相似,不再贴出运行结果

3:join()

在一个当前线程中,由另一个子线程调用join()方法,当前线程释放锁,当前线程必须要等待子线程执行完毕后才能继续执行。

(1):未采用join()方法

package hhxy;
import java.util.Scanner;
class  MyRunnable implements Runnable{
	private String str="";
	public void run() {
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Scanner in=new Scanner(System.in);
		System.out.println("输入字符串");
		str=in.next();
	}
	public String getStr() {
		return str;
	}
}
public class Main {
	public static void main(String[] args) {
		MyRunnable myRun=new MyRunnable(); 
		Thread my = new Thread(myRun);
		my.start();
		System.out.println("我是主线程"+myRun.getStr());
	}
}

(2):采用join()方法

package hhxy;
import java.util.Scanner;
class  MyRunnable implements Runnable{
	private String str="";
	public void run() {
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Scanner in=new Scanner(System.in);
		System.out.println("输入字符串");
		str=in.next();
	}
	public String getStr() {
		return str;
	}
}
public class Main {
	public static void main(String[] args) throws InterruptedException {
		MyRunnable myRun=new MyRunnable(); 
		Thread my = new Thread(myRun);
		my.start();
		my.join();
		System.out.println("我是主线程"+myRun.getStr());
	}
}

五:何为死锁

所有的的线程都处于等待阻塞状态,等待其他的线程去唤醒自己,但是已经不存在正在运行的线程去唤醒它们,此时便会造成死锁,而程序不结束运行,无限等待下去。

package hhxy;

import java.io.SyncFailedException;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
class Sale{
	private int sum=20;
	private Condition con;
	private ReentrantLock lock =new ReentrantLock();
	public int getSum() {
		sum--;
		return sum;
	}
	public void sale() throws InterruptedException
	{
		while(sum>0)
		{
			synchronized (this) {
				if(sum<=0)
					break;
				if(sum%4==1)
					wait();
				Thread.sleep(500);
				System.out.println(Thread.currentThread().getName()+"卖出编号:"+(sum--)+"的票");
			}
		}
	}
}
class  MyRunnable implements Runnable{
	private Sale sale=new Sale();
	public void run() {
		try {
			sale.sale();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
public class Main {
	public static void main(String[] args) {
		MyRunnable myRun=new MyRunnable(); 
		Thread lwk = new Thread(myRun,"lwk");
		Thread lz=new Thread(myRun,"lz"); 
		lwk.start();
		lz.start();
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值