设为首页 加入收藏

TOP

JAVA编程之多线程
2019-05-11 01:38:56 】 浏览:39
Tags:JAVA 编程 线程
一、线程相关概念
程序:是为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象。
进程:是程序的一次执行过成程,或是正在运行的一个程序。动态过程:有它自身的差生,存在和消亡的过程。
程序是静态的,进程是动态的。
线程:进程可进一步细化为线程,是一个程序内部的一条执行路径
若一个程序可同一时间执行多个线程,就是支持多线程
每个java程序有一个隐含的线程就是main
Java 提供了三种创建线程的方法:
  • 通过实现 Runnable 接口;
  • 通过继承 Thread 类本身;
  • 通过 Callable 和 Future 创建线程。
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
二、线程的优先级
每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。
具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。

实现Runnable接口创建线程:
package com.cnpc.Thread;
//创建一个线程方法一:实现Runnable接口
//实现的方式优于继承的方式:避免了java单继承的局限性
// 如果多个线程要操作同一份数据,更适合使用实现的方式
class RunnableDemo implements Runnable{
private Thread thread;
private String threadName;
RunnableDemo(String name) {
this.threadName=name;
System.out.println("正在创建线程: "+threadName);
}
public void run() {
System.out.println("正在运行:"+threadName);
try {
for(int i=4;i>0;i--) {
System.out.println("当前线程:"+threadName+", "+i);
System.out.println("当前线程是否活动"+thread.isAlive());
//让线程睡眠一会儿
Thread.sleep(1000);
}
} catch (InterruptedException e) {
System.out.println("线程: "+threadName+" 中断。。。");
}
System.out.println("线程:"+threadName+" 退出");
}
public void start() {
System.out.println("正在开始 "+threadName);
if(thread==null) {
thread=new Thread(this,threadName);
thread.start();
}
}
}
//Thread.yield(),暂停当前正在执行的线程对象,并执行其他对象。
//Thread.currentThread(),返回当前正在执行的线程的引用
public class RunnableThread {
public static void main(String[] args) {
RunnableDemo R1=new RunnableDemo("线程-1");
R1.start();
RunnableDemo R2=new RunnableDemo("线程-2");
R2.start();
}
}

继承Thread方法创建线程:
package com.cnpc.Thread;
//创建线程方法二:集成Thread类
//Thread.currentThread().getName()
//Thread.currentThread().yield()
class ThreadDemo extends Thread{
private Thread thread;
private String threadName;
ThreadDemo(String Name) {
threadName=Name;
System.out.println("创建线程名为:"+threadName);
}
public void run() {
System.out.println("正在运行:"+threadName);
try {
for(int i=4;i>0;i--) {
System.out.println("当前线程:"+threadName+", "+i);
//让线程睡眠一会儿
Thread.sleep(1000);
}
} catch (InterruptedException e) {
System.out.println("线程: "+threadName+" 中断。。。");
}
System.out.println("线程:"+threadName+" 退出");
}
public void start () {
System.out.println("开始 " + threadName );
if (thread == null) {
thread = new Thread (this, threadName);
thread.start ();
}
}
}
public class ExtendThreadThread {

public static void main(String[] args) {
ThreadDemo T1 = new ThreadDemo( "Thread-1");
T1.start();
ThreadDemo T2 = new ThreadDemo( "Thread-2");
T2.start();
}
}

Thread的常用方法:
1.start():启动线程并执行相应的run()方法
2.run():子线程要执行的代码放入run()
3.currentThread():静态的,调取当前的现场
4.getName():获取此线程的名字
5.setName():设置此线程的名字
6.yield():调用此方法的线程释放当前cpu的执行权
7.join():在A线程调用B线程的join方法,表示当执行到此,A线程停止执行,B线程参与进来,直至B线程执行完毕再执行A线程
8.isAlive():判断当前线程是否活动
9.sleep(long l)显式的使当前线程睡眠
10.线程通信:wait(),notify(),notifyAll()

三、线程调度策略
1.时间片
2.抢占式:高优先级的优先使用cpu
使用多线程的优点
只使用单个线程完成多个任务,肯定比用多个线程完成的时间更短,为何仍需多线程呢
优点:1.提高应用程序的响应,对图形化界面更有意义,可增强用户体验。
2.提高计算机系统CPU的利用率
3.改善程序结构,将既长又复杂的进程分为多个线程,独立运行,利于理解和修改。
守护线程:用来服务用户线程的,通过在start()前调用thread.setDeamen().j将用户线程设置成守护线程,守护线程最典型的例子是java虚拟机后台的垃圾回收机制。
四、线程的生命周期
  • 新建状态:
使用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序start()这个线程。
  • 就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
  • 运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
  • 阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
  • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
  • 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
  • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
  • 死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
start() 获得cpu执行权 正常执行完run()/Error未处理
新建 ----------> 就绪 <------------->运行 ---------------->死亡
失去CPU执行权
yield()

五、多线程安全问题范例
//模拟火车站售票,开启三个窗口售票,总票数为100张
//以继承Thread类来创建线程
//此程序会出现错票和重票问题
//实现Runnable接口可以使用this关键字作为锁,但继承Thread类实现同步得将锁设置为静态的static对象

/*//模拟火车站售票,开启三个窗口售票,总票数为100张
//以实现Runnable的方式创建Thread
//此程序会出现错票和重票问题
//1.线程安全问题存在的原因
// 由于一个线程在操作共享数据的过程中,未执行完的情况下,另外的线程参与进来,导致共享数据存在安全问题。
//2.如何解决线程安全问题
// 必须让一个线程操作共享数据完毕以后,其他线程才有机会参与共享数据的操作
//3.java如何实现解决线程安全问题
* 1.同步代码块
* synchronize(同步监视器){
* //需要被同步的代码块(既为操作共享数据的代码)
* }
* 共享数据:多个线程共同操作的变量
* 同步监视器:由任何一个类的对象来充当, 哪个线程获取此监视器,谁就执行大括号里被同步的代码,俗称:锁(多线程必须使用同一个锁)
* 实现Runnable接口可以使用this关键字作为锁,但继承Thread类实现同步得将锁设置为静态的static对象
* 2.同步方法
方法加synchronize关键字
*/
实现方法实现
class WindowImp implements Runnable
{
//100张票是三个窗口共享 ,如果不设置成static变量
static int ticket=100;
Object object=new Object();
public void run() {
while(true) {
// synchronized (object) {
synchronized (this) { //后面的主程序中wi只创建了一个,所以可以作为一把锁
}
if(ticket >0) {
try {
Thread.currentThread().sleep(100);;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"售票,票号为: "+ticket--);
}else {
break;
}
}
}
}
public class ImpTicketing {

public static void main(String[] args) {
WindowImp wi =new WindowImp();
Thread thread1=new Thread(wi);
Thread thread2=new Thread(wi);
Thread thread3=new Thread(wi);
thread1.start();
thread2.start();
thread3.start();
}

}
继承方法来实现
package com.cnpc.Thread;
//模拟火车站售票,开启三个窗口售票,总票数为100张
//以继承Thread类来创建线程
//此程序会出现错票和重票问题


//实现Runnable接口可以使用this关键字作为锁,但继承Thread类实现同步得将锁设置为静态的static对象
class Window extends Thread
{
static int ticket=100;
static Object object=new Object();
public void run() {
while(true) {
// synchronized (this) {
// //继承Thread类实现同步得将锁设置为静态的static对象,这里不能使用this
// }
synchronized (object) {
if(ticket >0) {
System.out.println(Thread.currentThread().getName()+"售票,票号为: "+ticket--);
}else {
break;
}
}
}
}
}

public class ExtendsTicketing {

public static void main(String[] args) {
Window w1=new Window();
Window w2=new Window();
Window w3=new Window();
w1.setName("窗口1");
w2.setName("窗口2");
w3.setName("窗口3");
w1.start();
w2.start();
w3.start();
}
}
释放锁的操作
1.当前线程的同步方法、同步代码块执行结束
2.当前线程在同步代码块,同步方法中遇到break、return终止了该代码块、该方法的继续执行
3.当前线程中出现了ERROR或Exception,导致异常结束
4.当前线程在同步代码块、同步方法中执行了线程对象的wait方法,当前线程暂停,并释放锁。
不会释放锁的操作
1.执行当前线程的Thread.sleep()、Thread.suspend()方法不会释放当前锁,只是睡眠。

package com.cnpc.Thread;


/*
* 银行有一个账户,有两个储户分别向同一个账户存3000,每次存1000,每次存完打印余额
*/
class Account{
static double balance;
public Account() {}
public synchronized void deposit(double amount){
balance=balance+amount;
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+balance);
}
}
class Customer extends Thread{
Account account;
public Customer(Account account) {
this.account=account;
}
public void run() {
for(int i=0;i<3;i++) {
account.deposit(1000);
}
}
}
public class BankThread {

public static void main(String[] args) {
// TODO Auto-generated method stub
Account account=new Account();
Customer c1=new Customer(account);
Customer c2=new Customer(account);
c1.setName("储户1");
c2.setName("储户2");
c1.start();
c2.start();
}
}



死锁:
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
解决:
专门的算法原则
尽量减少同步资源的定义

下面是死锁的范例
package com.cnpc.Thread;

public class DeadLock {
static StringBuffer sb1=new StringBuffer();
static StringBuffer sb2=new StringBuffer();
public static void main(String[] args) {
new Thread() {
public void run() {
synchronized (sb1) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sb1.append("A");
synchronized (sb2) {
sb2.append("B");
System.out.println(sb1);
System.out.println(sb2);
}
}
}
}.start();
new Thread() {
public void run() {
synchronized (sb2) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sb2.append("C");
synchronized (sb1) {
sb1.append("D");
System.out.println(sb1);
System.out.println(sb2);
}
}
}
}.start();
}
}

线程通信:
java.lang.Object提供的方法,只有在synchronize方法或synchronize代码块中才可使用
wait() :令当前线程挂起并放弃CPU、同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问
notify() :唤醒正在排队等待同步资源的线程中优先级最高者等待结束
notifyAll() :唤醒正在等待资源的所有线程结束等待

生产者消费者问题
/*
* 生产者与消费者问题:
* producer将产品交给clerk,而consumer从clerk处取走产品
* 店员一次只能持有固定数量的产品比如(20),如果producer试图生产更多的产品,clerk会叫producer停一下,
* 如果店中有空位放产品了再通知producer继续生产,如果店中没有产品了,clerk会通知consumer等一下
*/
//产品数量是共享数据,所以设计到同步,而且存在生产者和消费者的通信
class Clerk{
int product;
public synchronized void addProduct() {
//生产产品
if(product>=20) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else {
product++;
System.out.println(Thread.currentThread().getName()+":生产了第 "+product+"个产品");
notifyAll();
}
}
public synchronized void consumeProduct() {
//消费产品
if(product<=0)
{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else {
System.out.println(Thread.currentThread().getName()+":消费了第 "+product+"个产品");
product--;
notifyAll();
}
}
}


class Producer implements Runnable{
Clerk clerk;
public Producer(Clerk clerk) {
this.clerk=clerk;
}
public void run() {
System.out.println("生产者开始生产产品");
while(true) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
clerk.addProduct();
}
}
}
class Consumer implements Runnable{
Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk=clerk;
}
public void run() {
System.out.println("消费者消费产品");
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
while(true) {
clerk.consumeProduct();
}
}
}
public class ProduceConsumeThread {

public static void main(String[] args) {
Clerk clerk=new Clerk();
Producer producer1=new Producer(clerk);
Producer producer2=new Producer(clerk);
Consumer consumer =new Consumer(clerk);
Thread t1=new Thread(producer1);
Thread t2=new Thread(producer2);
Thread t3=new Thread(consumer);
t1.setName("生产者1");
t2.setName("生产者2");
t3.setName("消费者");
t1.start();
t2.start();
t3.start();

}

}

单例模式线程问题:
package com.cnpc.Thread;
/*懒汉式的单例线程安全问题
*
*/
class Singleton{
private static Singleton instance=null;
private Singleton() {
}
public static Singleton getInstance() {
if(instance==null) { //使用同步锁效率较低,此判断可提高效率
synchronized (Singleton.class) {
if(instance==null) {
instance=new Singleton();
}
}
}
return instance;
}
}
public class SingletonThread {
public static void main(String[] args) {
Singleton s1=Singleton.getInstance();
Singleton s2=Singleton.getInstance();
System.out.println(s1==s2);
}
}
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇基于Mysql5.7实现查找附近的店铺 下一篇springboot打war包去掉tomcat的ja..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目