(6) Java多线程 线程状态等相关知识(含单例懒汉式饿汉式)

(1)-------------------------------------------------------------------------

一. 线程状态类型

1. 新建状态(New):新创建了一个线程对象。
2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
二. 线程状态图

(2)-------------------------------------------------------------------------

  Runnable接口和Thread类

 //Runnable 接口只定义了一个run方法

 public  interface  Runnable{
    public abstract void run();
 }

 //Thread 类实现了Runnable 接口
 public  class Thread implements Runnable {
......
}

(3)-------------------------------------------------------------------------

1 进程和线程之间有什么不同?

进程:是一个正在执行中的程序。
  每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。线程在控制着进程的执行。


2用户线程和守护线程有什么区别?

当我们在Java程序中创建一个线程,它就被称为用户线程。一个守护线程是在后台执行并且不会阻止JVM终止的线程。
(垃圾回收的线程就是守护线程)。
当没有用户线程在运行的时候,JVM关闭程序并且退出。一个守护线程创建的子线程依然是守护线程。

void setDaemon(boolean on)  将该线程标记为守护线程或用户线程。 on - 如果为true,则将该线程标记为守护线程

3如何创建一个线程? 两种方式

有两种创建线程的方法:
一是实现Runnable接口,复写run方法,然后将它传递给Thread的构造函数,创建一个Thread对象 调用Thread对象的start方法
二是直接继承Thread类,复写run方法。

创建线程由Thread或者其子类来实现

继承Thread类示例
package com.pjm.testjava.thread.one1;
/*
进程:是一个正在执行中的程序。
      每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。线程在控制着进程的执行。

一个进程中至少有一个线程。

Java VM  启动的时候会有一个进程java.exe.
         该进程中至少一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。
   扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。


1,如何在自定义的代码中,自定义一个线程呢?

通过对api的查找,java已经提供了对线程这类事物的描述。就Thread类。

创建线程的第一种方式:继承Thread类。
步骤:
1,定义类继承Thread。
2,复写Thread类中的run方法。
   目的:将自定义代码存储在run方法。让线程运行。

3,调用线程的start方法,
   该方法两个作用:1.启动线程,2.调用run方法。

发现运行结果每一次都不同。
因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。
明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的效果。
我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。

这就是多线程的一个特性:
随机性。谁抢到谁执行,至于执行多长,cpu说的算。


为什么要覆盖run方法呢?

Thread类用于描述线程。
该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。
也就是说Thread类中的run方法,用于存储线程要运行的代码。


*/


//1 继承Thread类   extends Thread
public class ExtendThread1 extends Thread{
	
	//2 复写   Thread类中的 run方法 (需要线程执行的代码 写在run方法之中)
	@Override
	public void run(){
		for(int x=0; x<100; x++){
			System.out.println("ExtendThread1 run----"+x);
		}
	}
	
	public static void main(String[] args) {

		ExtendThread1 d = new ExtendThread1();//创建好一个线程对象。
		d.start();//开启一个新线程并执行该线程的run方法。
		//d.run();//仅仅是对象调用run方法。还是只有主线程在 执行。
		
		
		//主线程执行方法
		for(int x=0; x<600; x++){
			System.out.println("Main Thread!--"+x);
		}
	}
}

实现Runnable接口

package com.pjm.testjava.thread.one1;

/*
 * 实现Runnable接口
 */

//1 实现Runnable接口   implements Runnable
public class ImplementsRunnable2 implements Runnable {

	// 实现接口中的run方法(需要线程执行的代码 写在run方法之中)
	@Override
	public void run() {
		for (int x = 0; x < 600; x++) {
			System.out.println("ExtendThread1 run----" + x);
		}
	}

	public static void main(String[] args) {

		// 创建线程由Thread或者其子类来实现
		Thread t = new Thread(new ImplementsRunnable2());
		t.start();

		// 主线程执行方法
		for (int x = 0; x < 600; x++) {
			System.out.println("Main Thread!--" + x);
		}
	}
}

4线程生命周期?

当我们在Java程序中新建一个线程时,它的状态是New。当我们调用线程的start()方法时,状态被改变为Runnable。线程调度器会为Runnable线程池中的线程分配CPU时间并且讲它们的状态改变为Running。其他的线程状态还有Blocked ( 等待阻塞、同步阻塞、其他阻塞)和Dead。 线程生命周期   线程状态转换图

5可以直接调用Thread类的run()方法么?

当然可以,但是如果我们调用了Thread的run()方法,它的行为就会和普通的方法一样,
为了在新的线程中执行我们的代码,必须使用Thread.start()方法。
tart()方法两个作用 1 启动一个线程,2 调用线程对象的run方法 ,run方法中存储该线程需要执行的代码。
-------------------------------------------------------------------------

1)现在有T1T2T3三个线程,你怎样保证T2T1执行完后执行,T3T2执行完后执行?

这个线程问题通常会在第一轮或电话面试阶段被问到,目的是检测你对”join”方法是否熟悉。

这个多线程问题比较简单,可以用join方法实现。

/*
 join:<span style="font-family: Arial, Helvetica, sans-serif;">可以用来临时加入线程执行。</span>

 当A线程执行到了B线程的.join()方法时,
 A就会等待。等B线程都执行完,A才会执行。
 (万一B wait()了无人唤醒 , 主线程永远被冻结 ??  interrupt方法就显示威力 清除冻结状态)

yield
public static void yield()暂停当前正在执行的线程对象,并执行其他线程。

 */

class Demo implements Runnable {
	public void run() {
		for (int x = 0; x < 70; x++) {
			
			//toString()返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
			//Thread[Thread-1,5,main]
			System.out.println(Thread.currentThread().toString() + "....." + x);
		}
	}
}

public class JoinDemo27 {
	public static void main(String[] args) throws Exception {
		Demo d = new Demo();
		Thread t1 = new Thread(d);
		Thread t2 = new Thread(d);
		t1.start();

		//当A线程执行到了B线程的.join()方法  (主线程调用了t1线程的join方法)
		t1.join();  
		
		t2.start();

		//t1.join(); //join的位置 此时t1 t2两个线程在运行  主线程等t1结束才允许
		
		for (int x = 0; x < 80; x++) {
			 System.out.println("main....."+x);
		}
		System.out.println("over");
	}
}


2)JavaLock接口比synchronized块的优势是什么?

你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,

你会怎样去实现它?

lock接口在多线程和并发编程中最大的优势是它们为读和写分别提供了锁,它能满足你写像ConcurrentHashMap这样的高性能数据结构和有条件的阻塞。Java线程面试的问题越来越会根据面试者的回答来提问。我强烈建议在你去参加多线程的面试之前认真读一下Locks,因为当前其大量用于构建电子交易终统的客户端缓存和交易连接空间。

/*
 <strong>JDK1.5 中提供了多线程升级解决方案。
 将同步Synchronized替换成现实Lock操作。
 将Object中的wait,notify notifyAll,替换了Condition对象。
 该对象可以Lock锁 进行获取。
 该示例中,实现了本方只唤醒对方操作。

 Lock:替代了Synchronized
 lock 
 unlock
 newCondition()

 Condition:替代了Object wait notify notifyAll
 await();
 signal();
 signalAll();</strong>
 */

class Resource2x {
	private String name;
	private int count = 1;
	private boolean flag = false;
	// t1 t2
	private Lock lock = new ReentrantLock();

	private Condition condition_pro = lock.newCondition();//生产者condition_pro
	private Condition condition_con = lock.newCondition();//消费者condition_con

	public void set(String name) throws InterruptedException {
		lock.lock();
		try {
			while (flag)
				condition_pro.await();// t1,t2
			this.name = name + "--" + count++;

			System.out.println(Thread.currentThread().getName() + "...生产者.."+ this.name);
			flag = true;
			condition_con.signal();  //唤醒消费者
		} finally {
			lock.unlock();// 释放锁的动作一定要执行。
		}
	}

	// t3 t4
	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 Producer2 implements Runnable {
	private Resource2x res;

	Producer2(Resource2x res) {
		this.res = res;
	}

	public void run() {
		while (true) {
			try {
				res.set("+商品+");
			} catch (InterruptedException e) {
			}

		}
	}
}

class Consumer2 implements Runnable {
	private Resource2x res;

	Consumer2(Resource2x res) {
		this.res = res;
	}

	public void run() {
		while (true) {
			try {
				res.out();
			} catch (InterruptedException e) {
			}
		}
	}
}
public class ProducerConsumerDemo24 {

	public static void main(String[] args) {
		Resource2x r = new Resource2x();

		Producer2 pro = new Producer2(r);
		Consumer2 con = new Consumer2(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();
	}
}


3)javawaitsleep方法的不同?

通常会在电话面试中经常被问到的Java线程面试问题。

最大的不同是在等待时wait会释放锁,而sleep一直持有锁。

Wait通常被用于线程间交互,sleep通常被用于暂停执行。



4)用Java实现阻塞队列。

这是一个相对艰难的多线程面试问题,它能达到很多的目的。

第一,它可以检测侯选者是否能实际的用Java线程写程序;

第二,可以检测侯选者对并发场景的理解,并且你可以根据这个问很多问题。

如果他用wait()notify()方法来实现阻塞队列,

你可以要求他用最新的Java 5中的并发类来再写一次。


5)用Java写代码来解决生产者——消费者问题。

/*
 * 生产者 消费者问题( wait  notifyAll)
 * 
 * 多个线程负责生产
 * 多个线程负责消费
 *  
 * (之前的连个程序 就是  两个线程  一个负责生产   一个负责消费  用的  wait  notify)
 *  
 * (notify时)
 *  生产131
 *  生产132
 *  消费132(生产的131被覆盖掉  未被消费)
 *  生产线程t1 t2 消费线程t3 t4
 *  原因分析: t1调用notify方法是唤醒的是本方的线程t2,这时t2又不判断标记(每次醒来都不判断).t1刚刚生产完 t2又生产了一个。
 *  解决   if(flag)---->while(flag)  醒来再次进行判断标记flag    (全等待Bug 区别于 死锁)
 *      notify ----> notifyAll   						 (解决掉--全等待Bug)
 *  */

class Resource
{
	private String name;
	private int count = 1;
	private boolean flag = false;
	//  t1    t2
	public synchronized void set(String name)
	{
		while(flag)
			try{this.wait();}catch(Exception e){}//t1(放弃资格)  t2(获取资格)
		
		this.name = name+"--"+count++;
		System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
		flag = true;
		this.notifyAll();
	}

	//  t3   t4  
	public synchronized void out()
	{
		while(!flag)
			try{wait();}catch(Exception e){}//t3(放弃资格) t4(放弃资格)
		
		System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
		flag = false;
		this.notifyAll();
	}
}

class Producer implements Runnable {
	private Resource res;
	Producer(Resource res) {
		this.res = res;
	}

	public void run() {
		while (true) {
			res.set("+商品+");
		}
	}
}

class Consumer implements Runnable {
	private Resource res;
	Consumer(Resource res) {
		this.res = res;
	}

	public void run() {
		while (true) {
			res.out();
		}
	}
}

public class ProducerConsumerDemo23 {
	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();

	}
}

6)用Java编程一个会导致死锁的程序,你将怎么解决?

/**
  死锁。
  同步中嵌套同步。而持有的锁却不同。
 *写一个死锁程序。
 */
class Test implements Runnable {
	private boolean flag;

	Test(boolean flag) {
		this.flag = flag;
	}

	public void run() {
		if (flag) {
			while (true) {
				synchronized (MyLock.locka) {
					System.out.println(Thread.currentThread().getName()+ "...if locka ");
					synchronized (MyLock.lockb) {
						System.out.println(Thread.currentThread().getName()+ "..if lockb");
					}
				}
			}
		} else {
			while (true) {
				synchronized (MyLock.lockb) {
					System.out.println(Thread.currentThread().getName()+ "..else lockb");
					synchronized (MyLock.locka) {
						System.out.println(Thread.currentThread().getName()+ ".....else locka");
					}
				}
			}
		}
	}
}

class MyLock {
	static Object locka = new Object();
	static Object lockb = new Object();
}

public class DeadLockTest11 {
	public static void main(String[] args) {
		Thread t1 = new Thread(new Test(true));
		Thread t2 = new Thread(new Test(false));
		t1.start();
		t2.start();
	}
}

7) 什么是原子操作,Java中的原子操作是什么?

非常简单的java线程面试问题,接下来的问题是你需要同步一个原子操作。

所谓原子操作是指不会被 线程调度 机制打断的操作;这种操作一旦开始,就一直运行到结束, 中间不会有任何 context switch (切换到另一个线程)。

8)手写单例模式 懒汉式饿汉式

package com.pjm.testjava.thread.one1;

/*
 //饿汉式。
 class Single
 {
 private static final Single s = new Single();  //严谨加final
 private Single(){}
 public static Single getInstance()
 {
 return s;
 }
 }
 */

//懒汉式
public class Singleton9 {

	private static Singleton9 s = null; // 此处不能加final 要理解final的含义
	private Singleton9() { }
	// 1 错误的写法 ---这样多线程访问时 可会生成多个对象
	/*
	 * public static Singleton9 getInstance(){ if(s==null){ -->A -->B s = new
	 * Singleton9(); } return s; }
	 */

	// 2 同步函数 (低效) 直接在方法上面加上synchronized ,正确但是低效
	/*
	 * public static synchronized Singleton9 getInstance(){ if(s==null){ s = new
	 * Singleton9(); } return s; }
	 */

	// 3 同步代码块 (低效) (这样写 和上面的 同步函数 一样 效率低)
	/*
	 * public static synchronized Singleton9 getInstance(){
	 * 
	 * synchronized (Singleton9.class) { if(s==null){ s = new Singleton9(); } }
	 * return s; }
	 */
	// 4 同步代码块 (高效) 在同步代码块外再加一层非空判断 减少判断锁的参数
	public static synchronized Singleton9 getInstance() {

		if (s == null) {
			// /
			synchronized (Singleton9.class) {
				if (s == null) {
					s = new Singleton9();
				}
			}
			// /
		}
		return s;
	}
	public static void main(String[] args) {
		System.out.println("Hello World!");
	}
}







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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值