1. 多线程:如果一个程序的执行路径有多条.
单线程:程序的执行路径只有一条
2.面试题:
Jvm(java虚拟机)是多线程程序吗?
是多线程程序,由于java虚拟机中自带一个垃圾回收器,来确保程序不会轻易的造成内存溢出!
至少开启两条子线程:a、当前程序在执行代码的时候,会开启main:主线程
b、 垃圾回收器会开启一个垃圾回收线程,来确保程序不会内存异常,将不用的变量或者没有更多引用的对象来回收掉!
3.多线程的三种实现方式:
方式1:
1)自定一个类:MyThread 继承自Thread类
2)在MyThread类中重写Thread类中的run() :为什么重写run()
3)在主线程中,创建该类的实例对象
4)启动线程
注意:
1)启动线程使用start(),调用run()相当于调用普通方法,start()的调用其实质是通过jvm调用的底层run()方法
2)一个线程不能启动多次,否则非法线程异常
/**
*
*/
package org.zst.thread;
/**
* @author:zsyht
* @date:2017-12-8上午6:59:45
*/
public class demo extends Thread {
@Override
public void run() {
// System.out.println("helloworld");
//耗时的操作,线程睡眠,线程等待,循环语句
for(int x = 0 ; x <100 ; x ++){
System.out.println(x);
}
}
}
/**
*
*/
package org.zst.thread;
/**
* @author:zsyht
* @date:2017-12-8上午7:02:40
*/
public class Test {
public static void main(String[] args) {
//创建该线程的实例对象
//启动线程
/**
* 启动线程不是调用run()方法,
* strat()是线程开始执行的方法
run()方法调用相当于调用了一个普通方法,并不会出现线程随机性;而start()方法调用,
其实是通过Jvm调用线程中的run()来进行多个线程抢占CPU执行权
*/
// my.start() ;
//0java.lang.IllegalThreadStateException
/**
* 第二次启动线程,会出现:非法线程状态异常
* 当前线程已经启动了,不能重新启动
*/
demo d1 = new demo() ;
demo d2 = new demo() ;
//分别启动线程
d1.start() ;
d2.start() ;
}
}
public final String getName()返回该线程的名称。
设置线程名称
public final void setName(String name)改变线程名称,使之与参数 name 相同。
/**
*
*/
package org.zst.thread2;
/**
* @author:zsyht
* @date:2017-12-8上午7:07:42
*/
public class demo extends Thread{
//无参构造
public demo(){
}
//有参构造
public demo(String name){
super(name) ;
}
@Override
//d1和d2子线程都会执行这段代码,两个子线程在互相抢占CPU的执行权
public void run() {
for(int x = 0 ; x <200; x ++){
System.out.println(getName() +":"+x);
}
}
}
/**
*
*/
package org.zst.thread2;
/**
* @author:zsyht
* @date:2017-12-8上午7:21:32
*/
import java.security.AccessControlContext;
import java.security.AccessController;
public class Test {
public static void main(String[] args) {
//创建线程类的实例
/*
* 设置线程名称方式:无参+setName(String name)方法
* */
demo d1 = new demo() ;
demo d2 = new demo() ;
// public final void setName(String name)改变线程名称,使之与参数 name 相同。
d1.setName("左舒婷") ;
d2.setName("李赟琪") ;
//public static Thread currentThread():表示正在运行的线程
//默认所有的子线程的都在主线程中
System.out.println(Thread.currentThread().getName());//main
//启动线程
d1.start() ;
d2.start() ;
}
}
抛出异常throws InterruptedException等待该线程终止。
/**
*
*/
package org.zst.thread3;
/**
* @author:zsyht
* @date:2017-12-8上午8:03:53
*/
public class demo extends Thread {
@Override
public void run() {
for(int x = 0 ; x <100 ; x ++){
System.out.println(getName()+":"+x);
}
}
}
/**
*
*/
package org.zst.thread3;
/**
* @author:zsyht
* @date:2017-12-8上午8:05:32
*/
public class Test {
public static void main(String[] args) {
/*
* 创建该线程的实例对象
* */
demo d1 = new demo() ;
demo d2 = new demo() ;
demo d3 = new demo() ;
//设置线程名称
d1.setName("成都") ;
d2.setName("重庆") ;
d3.setName("上海") ;
//启动线程
d1.start() ;
//设置线程等待该线程终止该方法必须要启动线程
try {
d1.join() ;
} catch (InterruptedException e) {
//InterruptedException:中断异常
e.printStackTrace();
}
d2.start() ;
d3.start() ;
}
}
暂停当前线程执行其他线程,并不保证另一个线程就一定能抢占到CPU的执行权
/**
*
*/
package org.zst.thread4;
/**
* @author:zsyht
* @date:2017-12-8上午8:13:04
*/
public class demo extends Thread{
@Override
public void run() {
//yt1,yt2
for(int x =0 ; x <100 ; x ++){
System.out.println(getName()+":"+x);
Thread.yield() ;
}
}
}
/**
*
*/
package org.zst.thread4;
/**
* @author:zsyht
* @date:2017-12-8上午8:13:28
*/
public class Test {
public static void main(String[] args) {
//创建线程类对象
demo d1 = new demo() ;
demo d2 = new demo() ;
//设置线程名称
d1.setName("左舒婷") ;
d2.setName("李赟琪") ;
//启动线程
d1.start() ;
d2.start() ;
}
}
public final void setDaemon(boolean on) on指定true,就是设置守护线程...将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用。
jvm自动退出,对于主线程的数据如果直接输出完毕,对于两个守护线程来说不会立即消失,Jvm等会就自动退出.
/**
*
*/
package org.zst.thread5;
/**
* @author:zsyht
* @date:2017-12-8上午8:16:49
*/
public class demo extends Thread {
@Override
public void run() {
for(int x = 0 ; x <100 ; x ++){
System.out.println(getName()+":"+x);
}
}
}
/**
*
*/
package org.zst.thread5;
/**
* @author:zsyht
* @date:2017-12-8上午8:25:06
*/
public class Test {
public static void main(String[] args) {
//创建线程类对象
demo d1 = new demo() ;
demo d2 = new demo() ;
//设置线程名称
d1.setName("张飞") ;
d2.setName("关羽") ;
//setDaemon(boolean on)
d1.setDaemon(true) ;
d2.setDaemon(true) ;
//启动线程
d1.start() ;
d2.start() ;
Thread.currentThread().setName("刘备") ;
for(int x = 0 ; x <5 ; x++){
System.out.println(Thread.currentThread().getName()+":"+x); //设置主线程
}
}
}
线程睡眠:public static void sleep(long millis)
throws InterruptedException在指定的毫秒数内让当前正在执行的线程休眠(暂停执行
线程停止:
public final void stop():强迫线程停止执行
public void interrupt()中断线程。 表示中断线程一种状态
join()
yield()
stop()
setDeamon(boolean on):(用的多)
sleep():线程睡眠 (用的多)
wait():线程等待
多线程实现方式二(实际开发中第二种方式比第一种应用更加广泛,单继承具有局限性,该方法更符合Java的设计原则:数据分离原则):
a、 自定义一个类,该类实现Runnable接口
b、实现该接口中的run()方法
c、 在主线程中创建该类的实例对象
d、创建Thread对象,将3)创建的对象作为参数传递
e、分别启动线程
/**
*
*/
package org.zst_06;
/**
* @author:zst
* @date:2017-12-24下午1:21:56
*/
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int i =0;i<100;i++){
//此时获取线程名称不能用getName方法,getName只能在Thread中使用,此时使用会报错
//getName()方法在Runnable接口中没有定义
System.out.println(Thread.currentThread().getName()+":"+i);
//间接使用Thread类的静态功能获取线程名称
}
}
}
/**
*
*/
package org.zst_06;
/**
* @author:zst
* @date:2017-12-24下午1:33:43
*/
public class ThreadDemo {
public static void main(String [] args){
MyRunnable my = new MyRunnable();
//设置线程名称public Thread(Runnable target,String name)
Thread t1 = new Thread(my,"线程1");
Thread t2 = new Thread(my,"线程2");
//启动线程
t1.start();
t2.start();
}
}
面试题:多线程的实现方式有几种?
面试过程中答三种,口头回答两种
模拟电影院窗口售票
方式一:继承自Thread类
/**
*
*/
package org.zst_07;
/**
* @author:zst
* @date:2017-12-24下午2:23:30
*/
public class SellTicekts extends Thread {
//定义100张票
//这100张票应该被三个线程共用,所以用static修饰
private static int tickets = 100 ;
@Override
public void run() {
//有100张票
//模拟该电影院售票:电影院一直有票
while(true){
if(tickets>0){
System.out.println(getName() + "正在出售第" + (tickets--) + "张票");
}
}
}
}
/**
*
*/
package org.zst_07;
/**某电影院目前正在上映贺岁大片(红高粱,少林寺传奇藏经阁),共有100张票,而它有3个售票窗口售票,请设计一个程序
模拟该电影院售票。
方式一:继承自Thread类
* @author:zst
* @date:2017-12-24下午2:20:01
*/
public class SellTicketsDemo {
public static void main(String[] args) {
//创建线程类对象
SellTicekts st1 = new SellTicekts() ;
SellTicekts st2 = new SellTicekts() ;
SellTicekts st3 = new SellTicekts() ;
//分别命名三个窗口
st1.setName("窗口1") ;
st2.setName("窗口2") ;
st3.setName("窗口3") ;
//启动线程
st1.start() ;
st2.start() ;
st3.start() ;
}
}
方式二:实现Runnable接口
/**
*
*/
package org.zst_09;
/**
* @author:zst
* @date:2018-1-15下午5:45:40
*/
public class SellTicket implements Runnable {
//100张票被共用,不让外界修改数据
private static int tickets = 100 ;
// @Override
// public void run() {
//
// // 模拟电影院一直有票
// while (true) {
//
// //设置线程睡眠0.1m
//
// if (tickets > 0) {
// //ts1,ts2,ts3
// try {
// Thread.sleep(100) ;
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName() + "正在出售第"
// + (tickets--) + "张票");
// }
// }
// }
//一张票可能会被卖多次,(加入延迟操作之后)
// @Override
// public void run() {
//
// // 模拟电影院一直有票
// while (true) {
//
// //设置线程睡眠0.1m
//
// if (tickets > 0) {
// //ts1,ts2,ts3
// try {
// Thread.sleep(100) ;
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName() + "正在出售第"
// + (tickets--) + "张票");
// }
/**
* 理想状态:
* 窗口1正在出售第100张票
* 窗口2正在出售第99张票
* ...
*
* 多个线程在抢占CPU的执行权,CPU的一点点时间片具有特性:原子性操作(最简单最基本):比如变量++或者--
* 窗口1正在出售100张票
* ticketss--,之前的动作:记录之前的值100(窗口2)
* 窗口2在记录值的时候抢占cPU的执行权,打印-->窗口也正在出售第100张票
* -->99
*/
// }
@Override
public void run() {
// 模拟电影院一直有票
while (true) {
//设置线程睡眠0.1m
//ts1,ts2,ts3
try {
Thread.sleep(100) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售第"
+ (tickets--) + "张票");
}
/**
* 当前代码出现了负票
* 理想状态:
* 窗口1正在出售第3张票,tickets--
* 窗口3正在出售第2张票,tickets--
* 窗口2正在出售第1张票....
*
* 加入延迟操作后,线程执行具有随机性(延迟操作和随机性)
* 窗口2正在出售第0张票..tickets--
* 窗口3正在出售第-1张票...
*/
}
}
}
/**
*
*/
package org.zst_09;
/**
* @author:zst
* @date:2018-1-15下午5:46:29
*/
public class SellTicketDemo {
public static void main(String[] args) {
//创建SellTicket对象
SellTicket st = new SellTicket() ;
//创建线程类的对象
Thread t1 = new Thread(st, "窗口1") ;
Thread t2 = new Thread(st, "窗口2") ;
Thread t3 = new Thread(st, "窗口3") ;
//启动线程
t1.start() ;
t2.start() ;
t3.start() ;
}
}
为了模拟电影院卖票更真实的场景,每一个窗口卖票应该延迟操作
在接口自实现类中,在run()方法中让每一个线程执行睡眠0.1秒加入延迟操作:
1)一张票可能被卖多次
100张票会出现多次
CPU的执行具有原子性操作
2)可能出现负票
1,0,-1
延迟操作和线程随机性导致
检验多线程安全问题的标准(以后在判断一个多线程序是否有安全问题的标准)
1)当前是否是一个多线程环境
2)多线程环境中是否有共享数据
3)是否有多条语句对共享数据进行操作
就目前这个电影院卖票这个案例,
1)是多线程环境,因为要实现3个窗口同时卖票
2)有共享数据,比如:tickets
3)有多条语句对当前这个共享数据进行操作
优化改进:
1)多线程环境和共享数据改变不了,突破口就是3)条件:将多条语句对共享数据的操作进行更改
将多条语句对共享数据进行操作的代码用代码块包起来
Java的同步机制:
使用同步代码块:synchronized(同步锁对象){
多条语句对共享数据的操作;
}
同步锁对象:应该每一个线程都要使用这个锁对象(同步锁):理解为门的开和关
使用同步代码块可以解决线程安全问题
/**
*
*/
package org.zst_08;
/**
* @author:zst
* @date:2018-1-15下午5:29:55
*/
public class SellTicket implements Runnable {
//100张票被共用,不让外界修改数据
private static int tickets = 100 ;
//同步锁对象
private Object obj = new Object() ;
private Demo d = new Demo() ;
@Override
public void run() {
// 模拟电影院一直有票
while (true) {
//设置线程睡眠0.1m
//ts1,ts2,ts3
//相当于ts1,ts2,ts3创建自己的锁定(不是同一把锁)
// synchronized(new Object()){
synchronized(d){//每一个线程ts1,ts2,ts3都使用的一个锁
//ts1进来,其他线程进不来
if (tickets > 0) {
try {
Thread.sleep(100) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第"
+ (tickets--) + "张票");
}
}//ts1出来了
}
}
}
class Demo{
}
/**
*
*/
package org.zst_08;
/**
* @author:zst
* @date:2018-1-15下午5:30:47
*/
public class SellTicketDemo {
public static void main(String[] args) {
//创建SellTicket对象
SellTicket st = new SellTicket() ;
//创建线程类的对象
Thread t1 = new Thread(st, "窗口1") ;
Thread t2 = new Thread(st, "窗口2") ;
Thread t3 = new Thread(st, "窗口3") ;
//启动线程
t1.start() ;
t2.start() ;
t3.start() ;
}
}
同步锁定对象:1)可以Object类型以及任意的Java类型对象
2)如果一个方法进来之后是一个同步代码块,那么同步代码块可以演变成一个同步方法
3)如果是一个静态的同步方法,锁对象是当前类名class属性:类名.class (反射机制:获取一些类的字节码文件对象Class类对象)
面试题:
wait()线程等待,notify(),唤醒单个线程,notifyAll():唤醒所有线程这三个方法为什么不定义到Thread类中而是定义在Object类中?
线程中会存在安全问题,并且解决线程安全问题使用的同步代码块或者同步方法来解决,同步代码块来解决线程安全问题,就存在同步锁对象,谁能代表同步锁对象(Object以及任意的Java类),把它定义到Object类中;
等待唤醒机制(生产者和消费者线程) 线程安全的类有哪些?
Vector
StringBuffer
Hasttable
/**
*
*/
package org.zst_09;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
/**
* @author:zst
* @date:2018-1-15下午5:54:55
*/
public class ThreadDemo {
public static void main(String[] args) {
//1)集合里面的Vector集合
Vector<String> v = new Vector<String>() ;
//2)字符串缓冲区
StringBuffer sb = new StringBuffer("StringHello") ;
//3)Map集合中的子实现类:Hashtable
Hashtable<String, String> hm = new Hashtable<String,String>() ;
//从集合中来看,vector集合是一个安全的类,实际开发也不怎么用它
//Collections:针对集合操作的工具类
//public static <T> List<T> synchronizedList(List<T> list)返回指定列表支持的同步(线程安全的)列表
ArrayList list = new ArrayList() ;
List l = Collections.synchronizedList(list) ; //安全的集合列表
}
}