多线程的引入
线程:线程是程序执行的一条路径, 一个进程中可以包含多条线程;多线程并发 执行可以提高程序的效率, 可以同时完成多项工作
多线程的应用场景
红蜘蛛同时共享屏幕给多个电脑
迅雷开启多条线程一起下载
QQ同时和多个人一起视频
服务器同时处理多个客户端请求
多线程并行和并发的区别
·并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需 要多核CPU)
·并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安 排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。
·比如我跟两个网友聊天,左手操作一个电脑跟甲聊,同时右手用另一台电脑跟乙 聊天,这就叫并行。
·如果用一台电脑我先给甲发个消息,然后立刻再给乙发消息,然后再跟甲聊,再 跟乙聊。这就叫并发。
Java程序运行原理和JVM的启动是多线程的吗
Java程序运行原理:Java命令会启动java虚拟机,启动JVM,等于启动了一个 应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” ,然后 主线程去调用某个类的 main 方法。
JVM的启动是多线程的吗:JVM启动至少启动了垃圾回收线程和主线程,所以是多 线程的。
多线程程序实现的方式1
继承Thread:
定义类继承Thread
重写run方法
把新线程要做的事写在run方法中
创建线程对象
开启新线程, 内部会自动执行run方法
public class ThreadDemo {
public static void main(String[] args) {
//4,创建自定义类的对象
MyThreadmt = new MyThread();
//5,开启线程
mt.start();
//主线程和子线程会交替执行
for(int i=0;i<1000;i++){
System.out.println("bb");
}
}
}
//1,定义类继承Thread
class MyThread extends Thread{
//2,重写run方法
@Override
public void run() {
//3,将要执行的代码,写在run方法中
for(int i=0;i<1000;i++){
System.out.println("aaaaaaaaa");
}
}
}
多线程程序实现的方式2
实现Runnable
定义类实现Runnable接口
实现run方法
把新线程要做的事写在run方法中
创建自定义的Runnable的子类对象
创建Thread对象, 传入Runnable
调用start()开启新线程, 内部会自动调用Runnable的run()方法
public class ThreadDemo1 {
public static void main(String[] args) {
//4,创建自定义对象
MyRunnablemr = new MyRunnable();
//5,将其当作参数传递给Thread的构造函数
Threadt = new Thread(mr);
//6,开启线程
t.start();
for(int i = 0; i < 1000; i++) {
System.out.println("bb");
}
}
}
//1,定义一个类实现Runnable接口
class MyRunnable implements Runnable{
//2,重写run方法
@Override
public void run() {
//3,将要执行的代码写在run方法中
for(int i=0;i<1000;i++){
System.out.println("aaaaaaaaa");
}
}
}
实现Runnable的原理
1,看Thread类的构造函数,传递了Runnable接口的引用
2,通过init()方法找到传递的target给成员变量的target赋值
3,查看run方法,发现run方法中有判断,如果target不为null就会调用 Runnable接口子类对象的run方法
多线程两种方式的区别
查看源码的区别:
a.继承Thread : 由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法
b.实现Runnable : 构造函数中传入了Runnable的引用, 成员变量记住了 它, start()调用run()方法时内部判断成员变量Runnable的引用是否 为空, 不为空编译时看的是Runnable的run(),运行时执行的是子类的 run()方法
继承Thread
好处是:可以直接使用Thread类中的方法,代码简单
弊端是:如果已经有了父类,就不能用这种方法
实现Runnable接口
好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现 接口,而且接口是可以多实现的
弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到 Thread的方法,代码复杂
匿名内部类实现线程的两种方式
继承Thread类
//1,new 类(){}继承这个类
new Thread() {
//2,重写run方法
public void run() {
//3,将要执行的代码,写在run方法中
for(int i = 0; i < 3000; i++) {
System.out.println("aaaaa");
}
}
}.start();//4,开启线程
实现Runnable接口
//1,new 接口(){}实现这个接口
new Thread(new Runnable(){
//2,重写run方法
public void run() {
//3,将要执行的代码,写在run方法中
for(int i = 0; i < 3000; i++) {
System.out.println("bb");
}
}
}).start();//4,开启线程
线程获取名字和设置名字
获取名字:通过getName()方法获取线程对象的名字
设置名字:通过构造函数可以传入String类型的名字
设置名字方法一:
new Thread("xxx") {// 通过构造法方法给线程设置名字
public void run() {
for (int i = 0; i < 1000; i++) {
// 匿名内部类调用
// 通过getName方法获取线程名字
System.out.println(this.getName() + "....aaaaa");
}
}
}.start();
设置名字方法二:
new Thread() {
public void run() {
// 通过setName方法设置线程名字
this.setName("yyy");
for (int i = 0; i < 1000; i++) {
// 通过getName方法获取线程名字
System.out.println(this.getName() + "....bb");
}
}
}.start();
设置名字方法三:
Threadt = new Thread() {
public void run() {
for (int i = 0; i < 1000; i++) {
// 通过getName方法获取线程名字
System.out.println(this.getName() + "....bb");
}
}
};
// 通过setName方法设置线程名字
t.setName("zzz");
t.start();
获取当前线程的对象
new Thread(){
public void run() {
for(int i = 0; i < 1000; i++) {
//获取当前线程对对象
System.out.println(Thread.currentThread().
getName()+ "...aaaaaaaaa");
}
}
}.start();
new Thread(new Runnable() {
public void run() {
for(int i = 0; i < 1000; i++) {
//获取当前线程对象 System.out.println(Thread.currentThread().
getName()+ "...bb");
}
}
}).start();
//获取主函数线程的引用,并改名字
Thread.currentThread().setName("我是主线程");
//获取主函数线程的引用,并获取名字
System.out.println(Thread.currentThread().
getName());
休眠线程
Thread.sleep(毫秒), 控制当前线程休眠若干毫秒
new Thread() {
public void run() {
for(int i = 0; i < 10; i++) {
System.out.println(getName() + "...aaaaaaaa");
try {
//当前线程睡眠10毫秒
Thread.sleep(10);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
for(int i = 0; i < 10; i++) {
System.out.println(getName() + "...bb");
try {
//当前线程睡眠10毫秒
Thread.sleep(10);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
守护线程
setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守 护线程都执行结束后, 自动退出
Thread t1 = new Thread() {
public void run() {
for(int i = 0; i < 50; i++) {
System.out.println(getName() + "...aaaaaa");
try {
Thread.sleep(10);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Threadt2 = new Thread() {
public void run() {
for(int i = 0; i < 5; i++) {
System.out.println(getName() + "...bb");
try {
Thread.sleep(10);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
//设置守护线程必须在thread.start()之前设置
//不能把正在运行的常规线程设置为守护线程
//传入true将t1设置为守护线程,
t1.setDaemon(true);
t1.start();
t2.start();
加入线程
join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
join(int), 可以等待指定的毫秒之后继续
final Thread t1 = new Thread() {
public void run() {
for(int i = 0; i < 50; i++) {
System.out.println(getName() + "...aaaaaa");
try {
Thread.sleep(10);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Threadt2 = new Thread() {
public void run() {
for(int i = 0; i < 50; i++) {
if(i == 2) {
try {
//插队,加入,直至t1线程全部执行完成
//t1.join();
//加入,有固定的时间,过了固定时间,继续交替执行
t1.join(30);
Thread.sleep(10);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getName() + "...bb");
}
}
};
t1.start();
t2.start();
礼让线程
public class ThreadDemo3 {
public static void main(String[] args) {
new MThread().start();
new MThread().start();
}
}
class MThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
if (i % 10 == 0) {
//礼让线程,让出线程,但不一定有效果,线程执行由cup调度
Thread.yield();
}
System.out.println(getName() + "...." + i);
}
}
}
设置线程的优先级
setPriority()设置线程的优先级(效果不明显,因为线程调度是由cpu决定的)
Thread t1 = new Thread() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + "...aaaaa");
}
}
};
Threadt2 = new Thread() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + "...bb");
}
}
};
// 设置线程优先级,最大是10,最小是1,默认是5,
//可以给int值[1,10],也可以用Thread的常量
// t1.setPriority(10);
// t2.setPriority(1);
t1.setPriority(Thread.MIN_PRIORITY);
t2.setPriority(Thread.MAX_PRIORITY);
t1.start();
t2.start();
多线程同步代码块
什么情况下需要同步
a,当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程 中CPU不要切换到其他线程工作. 这时就需要同步.
b,如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行 结束之前, 不会执行另外一段代码.
同步代码块
a,使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步 代码块
b,多个同步代码块如果使用相同的锁对象, 那么他们就是同步的
public class ThreadDemo5 {
public static void main(String[] args) {
final Printer p = new Printer();
new Thread() {
@Override
public void run() {
while (true) {
p.print1();
}
}
}.start();
new Thread() {
@Override
public void run() {
while (true) {
p.print2();
}
}
}.start();
}
}
class Demo {}
class Printer {
Demo d = new Demo();
public void print1() {
// 锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁, //不能用匿名对象
synchronized (d) {
System.out.print("好");
System.out.print("好");
System.out.print("学");
System.out.print("习");
System.out.print("\r\n");
}
}
public void print2() {
synchronized (d) {
System.out.print("天");
System.out.print("天");
System.out.print("向");
System.out.print("上");
System.out.print("\r\n");
}
}
}
多线程同步方法
使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的
public class ThreadDemo6 {
public static void main(String[] args) {
final Printer2 p = new Printer2();
new Thread() {
@Override
public void run() {
while (true) {
p.print1();
}
}
}.start();
new Thread() {
@Override
public void run() {
while (true) {
p.print2();
}
}
}.start();
}
}
class Printer2 {
//非静态的同步方法的锁对象:this
//静态的同步方法的锁对象不可能是this(静态方法是优选于类对象存在[静态方法比类要先加载])
//静态的同步方法的锁对象:该类的字节码对象(.class文件)
//同步方法只需在方法上添加synchronized关键字即可
public static synchronized void print1() {
System.out.print("好");
System.out.print("好");
System.out.print("学");
System.out.print("习");
System.out.print("\r\n");
}
public static void print2() {
synchronized (Printer2.class) {
System.out.print("天");
System.out.print("天");
System.out.print("向");
System.out.print("上");
System.out.print("\r\n");
}
}
}
线程安全问题
多线程并发操作同一数据时, 就有可能出现线程安全问题,使用同步技术可以解 决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作
使用继承Thread
public class ThreadDemo7 {
public static void main(String[] args) {
TicketsSellert1 = new TicketsSeller();
TicketsSellert2 = new TicketsSeller();
TicketsSellert3 = new TicketsSeller();
TicketsSellert4 = new TicketsSeller();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t4.setName("窗口4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class TicketsSeller extends Thread {
private static int tickets = 100;
//成员变量作为锁对象,必须保证所用的锁是同一个,不然无效
static Object obj = new Object();
public TicketsSeller() {
super();
}
public TicketsSeller(String name) {
super(name);
}
public void run() {
while(true) {
// synchronized(obj){
synchronized(TicketsSeller.class) {
if(tickets <= 0)
break;
try {
//线程1睡,线程2睡,线程3睡,线程4睡
Thread.sleep(10);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + "...这是第" + tickets-- + "号票");
}
}
}
}
实现Runnable接口
public class ThreadDemo8 {
public static void main(String[] args) {
MyTicketmt = new MyTicket();
new Thread(mt,"窗口1").start();
new Thread(mt,"窗口2").start();
new Thread(mt,"窗口3").start();
new Thread(mt,"窗口4").start();
}
}
class MyTicket implements Runnable {
private int tickets = 100;
public void run() {
while(true) {
//可以使用this,因为只创建了一个对象,也就是说共用了一个锁对象
// synchronized(this){
synchronized(MyTicket.class) {
if(tickets <= 0)
break;
try {
Thread.sleep(10);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "...这是第" + tickets-- + "号票");
}
}
}
}
死锁
多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁,尽量 不要嵌套使用
private static String s1 = "筷子左";
private static String s2 = "筷子右";
public static void main(String[] args) {
new Thread() {
public void run() {
while(true) {
synchronized(s1) {
System.out.println(getName() +
"...拿到" + s1 + "等待" + s2);
synchronized(s2) {
System.out.println(getName() +
"...拿到" + s2 + "开吃");
}
}
}
}
}.start();
new Thread() {
public void run() {
while(true) {
synchronized(s2) {
System.out.println(getName() +
"...拿到" + s2 + "等待" + s1);
synchronized(s1) {
System.out.println(getName() +
"...拿到" + s1 + "开吃");
}
}
}
}
}.start();
}
以前的线程安全的类回顾
Vector,StringBuffer,Hashtable,Collections.synchroinzed(xxx)
Vector是线程安全的,ArrayList是线程不安全的
StringBuffer是线程安全的,StringBuilder是线程不安全的
Hashtable是线程安全的,HashMap是线程不安全的
单例设计模式
单例设计模式:保证类在内存中只有一个对象
如何保证类在内存中只有一个对象呢?
(1)控制类的创建,不让其他类来创建本类的对象。private
(2)在本类中定义一个本类的对象。Singleton s;
(3)提供公共的访问方式。 public static Singleton getInstance(){returns}
单例写法两种:
(1)饿汉式 开发用这种方式
//饿汉式(不管用不用都创建对象)
class Singleton{
//1,私有构造器,其他类不能访问该构造方法
privateSingleton(){}
//2,创建本类对象
private static Singleton s = new Singleton();
//3,对外提供公共的访问方法
public static Singleton getInstance(){
return s;
}
public static void print(){
System.out.println("------------");
}
}
(2)懒汉式 面试写这种方式。多线程的问题?
//懒汉式:单例的延迟加载模式(先声明不创建对象)
class Singleton1 {
//1,私有构造函数
privateSingleton1(){}
//2,声明一个本类的引用
private static Singleton1 s;
//3,对外提供公共的访问方法
public static Singleton1 getInstance() {
if(s == null)
//线程1等待,线程2等待(多线程访问会出现多次创建对象,有安全隐患)
s = new Singleton1();
return s;
}
public static void print() {
System.out.println("11111111111");
}
}
(3)第三种格式
class Singleton2 {
private Singleton2() {}
//final是最终的意思,被final修饰的变量不可以被更改
public static final Singleton2 s =
new Singleton2();
}
饿汉式和懒汉式的区别:
1,饿汉式:空间换时间,懒汉式:时间换空间
2,在多个线程访问时,饿汉式不会创建多个对象,而懒汉式有可能创建多个 对象
多线程Runtime类
//获取运行时对象(单列设置)
Runtimer = Runtime.getRuntime();
//执行字符串命名(关机)
r.exec("shutdown -s -t 300");
//取消执行命令
r.exec("shutdown -a");
Timer计时器
Timer:一种工具,线程用其安排以后在后台线程中执行的任务,可安排任 务执行一次,或者定期重复执行
public class ThreadDemo12 {
public static void main(String[] args) throws InterruptedException {
Timert = new Timer();
//指定时间执行任务(年:当前年份-1900,月:0-11,日:1-31,时:24小时制,分:0-59,秒:0-59)
t.schedule(new MyTimerTask(), new Date(117, 2, 13, 18, 31,30));
//从指定时间开始,隔3s时间后重复执行
//第一个参数是执行的任务,第二个参数是执行的时间,第三个参数是过多长再时间重复
t.schedule(new MyTimerTask(), new Date(117, 2, 13, 18, 31,30),3000);
while(true){
Thread.sleep(1000);
System.out.println(new Date());
}
}
}
class MyTimerTask extends TimerTask{
@Override
public void run() {
System.out.println("起床");
}
}
两个线程间的通信
什么时候需要通信
多个线程并发执行时, 在默认情况下CPU是随机切换线程的
如果我们希望他们有规律的执行, 就可以使用通信, 例如每个线程执行一次 打印
怎么通信
如果希望线程等待, 就调用wait()
如果希望唤醒等待的线程, 就调用notify();
这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用
public class ThreadDemo01 {
public static void main(String[] args) {
final Printer p = new Printer();
new Thread(){
public void run() {
while(true){
try {
p.print1();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread(){
public void run() {
while(true){
try {
p.print2();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
//等待唤醒机制
class Printer {
private int flag = 1;
public void print1() throws InterruptedException {
synchronized (this) {
if(flag!=1){
//当前线程等待
this.wait();
}
System.out.print("好");
System.out.print("好");
System.out.print("学");
System.out.print("习");
System.out.print("\r\n");
flag = 2;
//随机唤醒单个等待的线程
this.notify();
}
}
public void print2() throws InterruptedException {
synchronized (this) {
if(flag!=2){
this.wait();
}
System.out.print("天");
System.out.print("天");
System.out.print("向");
System.out.print("上");
System.out.print("\r\n");
flag = 1;
this.notify();
}
}
}
三个或三个以上间的线程通信
多个线程通信的问题
notify()方法是随机唤醒一个线程
notifyAll()方法是唤醒所有线程
JDK5之前无法唤醒指定的一个线程
如果多个线程之间通信, 需要使用notifyAll()通知所有线程, 用while来 反复判断条件
下面是JDK1.5版本之前的处理方案
public class ThreadDemo2 {
public static void main(String[] args) {
Printer1p = new Printer1();
new Thread(){
public void run() {
while(true){
try {
p.print1();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread(){
public void run() {
while(true){
try {
p.print2();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread(){
public void run() {
while(true){
try {
p.print3();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
/**
* 1,在同步代码块中,用哪个对象锁,就用哪个对象调用wait方法
* 2,为什么wait方法和notify方法定义在Object类中?
* 因为锁对象可以是任意对象,Object是所有的类的基类
* 所以wait方法和notify方法需要定义在object这个类中
* 3,sleep方法和wait方法的区别?
* a,sleep方法必须传入参数,参数就是时间,时间到了自动唤醒
* wait方法可以传入参数也可以不传入参数,传入参数就是在参数的时间结束后等待,不传入参数就是直接等待
* b,sleep在同步函数或同步代码块中不释放锁,wait方法在同步函数或者是同步代码块中释放锁
*
* @author LMD
*
*/
// 等待唤醒机制
class Printer1 {
private int flag = 1;
public void print1() throws InterruptedException {
synchronized (this) {
//if为啥换成while?因为wait方法在if(){wait}在哪里等待就在哪里起来,所以不会再进行像while那样的判断
while (flag != 1) {
// 当前线程等待
this.wait();
}
System.out.print("好");
System.out.print("好");
System.out.print("学");
System.out.print("习");
System.out.print("\r\n");
flag = 2;
// 随机唤醒单个等待的线程
// this.notify();
this.notifyAll();
}
}
public void print2() throws InterruptedException {
synchronized (this) {
while (flag != 2) {
this.wait();
}
System.out.print("天");
System.out.print("天");
System.out.print("向");
System.out.print("上");
System.out.print("\r\n");
flag = 3;
// this.notify();
this.notifyAll();
}
}
public void print3() throws InterruptedException {
synchronized (this) {
while (flag != 3) {//while是循环判断,每次都会判断标记
this.wait();
}
System.out.print("成");
System.out.print("为");
System.out.print("学");
System.out.print("霸");
System.out.print("\r\n");
flag = 1;
// this.notify();
this.notifyAll();
}
}
}
JDK1.5的新特性互斥锁
同步:使用ReentrantLock类的lock()和unlock()方法进行同步
通信:
1,使用ReentrantLock类的newCondition()方法可以获取Condition对象
2,需要等待的时候使用Condition的await()方法, 唤醒时用signal()方法
3,不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了
public classReentrantLockDemo {
public static void main(String[] args) {
Printer2p = new Printer2();
new Thread() {
public void run() {
while (true) {
try {
p.print1();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
while (true) {
try {
p.print2();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
while (true) {
try {
p.print3();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
class Printer2 {
private ReentrantLock r = new ReentrantLock();
// 创建监视器
private Condition c1 = r.newCondition();
private Condition c2 = r.newCondition();
private Condition c3 = r.newCondition();
private int flag = 1;
public void print1() throws InterruptedException {
// 获取锁
r.lock();
// 每次只唤醒一个线程,所以不用进行循环判断
if (flag != 1) {
c1.await();
}
System.out.print("好");
System.out.print("好");
System.out.print("学");
System.out.print("习");
System.out.print("\r\n");
flag = 2;
c2.signal();
// 释放锁
r.unlock();
}
public void print2() throws InterruptedException {
// 获取锁
r.lock();
if (flag != 2) {
c2.await();
}
System.out.print("天");
System.out.print("天");
System.out.print("向");
System.out.print("上");
System.out.print("\r\n");
flag = 3;
c3.signal();
// 释放锁
r.unlock();
}
public void print3() throws InterruptedException {
// 获取锁
r.lock();
if (flag != 3) {
c3.await();
}
System.out.print("成");
System.out.print("为");
System.out.print("学");
System.out.print("霸");
System.out.print("\r\n");
flag = 1;
c1.signal();
// 释放锁
r.unlock();
}
}
线程组的概述和使用
线程组概述
a,Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理, Java允许程序直接对线程组进行控制。
b,默认情况下,所有的线程都属于主线程组。
c,public final ThreadGroup getThreadGroup()//通过线程对象获取 他所属于的组
d,public final String getName()//通过线程组对象获取他组的名字
e,我们也可以给线程设置分组
1,ThreadGroup(Stringname) 创建线程组对象并给其赋值名字
2,创建线程对象
3,Thread(ThreadGroup?group,Runnable?target, String?name)
4,设置整组的优先级或者守护线程
代码示例:
public class ThreadGroupDemo{
public static void main(String[] args) {
// demo1();
//创建新的线程组
ThreadGrouptg = new ThreadGroup("我是一个新的线程组");
//创建Runnable的子类对象
MyRunnablemr = new MyRunnable();
//将线程t1放在线程组中
Threadt1 = new Thread(tg,mr,"张三");
//将线程t1放在线程组中
Threadt2 = new Thread(tg,mr,"李四");
//获取组名
System.out.println(t1.getThreadGroup().getName());
System.out.println(t2.getThreadGroup().getName());
//将整组变成守护线程
tg.setDaemon(true);
}
public static void demo1() {
MyRunnablemr = new MyRunnable();
Threadt1 = new Thread(mr,"张三");
Threadt2 = new Thread(mr,"李四");
//获取线程组
ThreadGrouptg1 = t1.getThreadGroup();
ThreadGrouptg2 = t2.getThreadGroup();
//默认是主线程
System.out.println(tg1.getName());
System.out.println(tg2.getName());
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().
getName()+"....."+i);
}
}
}
线程的五种状态
线程池的概述和使用
线程池概述:程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统 进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存 期很短的线程时,更应该考虑使用线程池。线程池里的每一个线程代码结束后, 并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。在 JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程 池
内置线程池的使用概述
1,JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
2,public staticExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池
3,public staticExecutorService newSingleThreadExecutor()
4,这些方法的返回值是ExecutorService对象,该对象表示一个线程池, 可以执行Runnable对象或者Callable对象代表的线程。它提供了如下 方法
5,Future<?>submit(Runnable task)
6,<T>Future<T> submit(Callable<T> task)
使用步骤:
创建线程池对象
创建Runnable实例
提交Runnable实例
关闭线程池
程序示例:
public class ThreadDemo3 {
public static void main(String[] args) {
// 创建线程池
ExecutorServicepool = Executors.newFixedThreadPool(2);
// 将线程放进线程池并执行
pool.submit(new MyRunnable1());
pool.submit(new MyRunnable1());
// 关闭线程池
pool.shutdown();
}
}
class MyRunnable1 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().
getName()+ "....." + i);
}
}
}
多线程程序实现的方式3
提交的是Callable(其他两种是:继承Thread类,实现Runnable接口)
public class ThreadCallable {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建线程池
ExecutorServicepool = Executors.newFixedThreadPool(2);
//将Callable的子类提交到线程池中,Future接收计算的结果
Future<Integer>f1 =pool.submit(new MyCallable(100));
Future<Integer>f2 = pool.submit(new MyCallable(50));
System.out.println(f1.get());
System.out.println(f2.get());
pool.shutdown();
}
}
class MyCallable implements Callable<Integer> {
private int num;
public MyCallable(int num) {
this.num = num;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= num; i++) {
sum += i;
}
return sum;
}
}
多线程程序实现的方式3的好处和弊端
好处:可以有返回值,可以抛出异常
弊端:代码比较复杂,所以一般不用
简单工厂模式概述和使用
简单工厂模式概述:又叫静态工厂方法模式,它定义一个具体的工厂类负责创建 一些类的实例
优点:客户端不需要在负责对象的创建,从而明确了各个类的职责
缺点:这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对 象的创建方式不同,就需要不断的修改工厂类,不利于后期的维护
public class AnimalFactory {
// public static Dog creatDog(){
// return new Dog();
// }
// public static Cat creatCat(){
// return new Cat();
// }
//上面方法定义很多,复用性太差
public static AnimalcreateAnimal(String name){
if("Dog".equals(name)){
return new Dog();
}else if("Cat".equals(name)){
return new Cat();
}else{
return null;
}
}
}
工厂方法模式的概述和使用
工厂方法模式概述:工厂方法模式中抽象工厂类负责定义创建对象的接口,具体 对象的创建工作由继承抽象工厂的具体类实现。
优点:客户端不需要在负责对象的创建,从而明确了各个类的职责,如果有新的 对象增加,只需要增加一个具体的类和具体的工厂类即可,不影响已有的 代码,后期维护容易,增强了系统的扩展性
缺点:需要额外的编写代码,增加了工作量
动物抽象类:public abstract class Animal{
public abstract void eat(); }
工厂接口:public interfaceFactory {
publicabstract Animal createAnimal();}
具体狗类:public class Dogextends Animal {}
具体猫类:public class Catextends Animal {}
(开始,在测试类中每个具体的内容自己创建对象,但是,创建对象的工作如果比 较麻烦,就需要有人专门做这个事情,所以就知道了一个专门的类来创建对象。 发现每次修改代码太麻烦,用工厂方法改进,针对每一个具体的实现提供一个具 体工厂)
狗工厂:public class DogFactory implements Factory {
public Animal createAnimal() {…}
}
猫工厂:public class CatFactory implements Factory {
public Animal createAnimal() {…}
}
具体使用
DogFactory df = new DogFactory();
df.eat();
如何创建一个窗口并显示
GraphicalUser Interface(图形用户接口)
//构造函数传入的是窗体title
Frame f = new Frame("my window");
//设置布局管理器
f.setLayout(new FlowLayout());
//设置窗体大小
f.setSize(500,400);
//设置窗体出现在屏幕的位置
f.setLocation(300,200);
//设置图标
f.setIconImage(Toolkit.getDefaultToolkit().
createImage("img.jpg"));
//设置窗体可见
f.setVisible(true);
布局管理器
FlowLayout(流式布局管理器)
从左到右的顺序排列; Panel默认的布局管理器。
BorderLayout(边界布局管理器)
东,南,西,北,中;Frame默认的布局管理器
GridLayout(网格布局管理器):规则的矩阵
CardLayout(卡片布局管理器):选项卡
GridBagLayout(网格包布局管理器):非规则的矩阵
Frame f = new Frame("my window");
Buttonb1 = new Button("按钮1");
f.add(b1);
//设置布局管理器
f.setLayout(new FlowLayout());
f.setVisible(true);
窗体监听
Frame f = new Frame("我的窗体");
//事件源是窗体,把监听器注册到事件源上
//事件对象传递给监听器
f.addWindowListener(new WindowAdapter() {
@Override
public voidwindowClosing(WindowEvent e) {
//退出虚拟机,关闭窗口
System.exit(0);
}
});
鼠标监听
Buttonb1 = new Button("按钮1");
b1.addMouseListener(new MouseAdapter() {
//鼠标释放
@Override
public voidmouseReleased(MouseEvent e) {
//退出虚拟机,关闭窗口
System.exit(0);
}
});
键盘监听和键盘事件
b1.addKeyListener(new KeyAdapter() {
@Override
public voidkeyReleased(KeyEvent e) {
//获取键盘按下的键值code,空格退出
if(e.getKeyCode()==KeyEvent.VK_SPACE)
System.exit(0);
}
});
动作监听
//添加动作监听,应用场景:暂停视频和播放视频
b1.addActionListener(new ActionListener() {
@Override
public voidactionPerformed(ActionEvent e) {
System.exit(0);
}
});
事件处理
事件: 用户的一个操作
事件源: 被操作的组件
监听器: 一个自定义类的对象, 实现了监听器接口, 包含事件处理方法,把监 听器添加在事件源上, 当事件发生的时候虚拟机就会自动调用监听器中 的事件处理方法
适配器设计模式
什么是适配器
1,在使用监听器的时候, 需要定义一个类事件监听器接口.
2,通常接口中有多个方法, 而程序中不一定所有的都用到, 但又必须重写, 这很繁琐.
3,适配器简化了这些操作, 我们定义监听器时只要继承适配器, 然后重写需 要的方法即可.
适配器原理
1,适配器就是一个类, 实现了监听器接口, 所有抽象方法都重写了, 但是方 法全是空的.
2,适配器类需要定义成抽象的,因为创建该类对象,调用空方法是没有意义的
3,目的就是为了简化程序员的操作, 定义监听器时继承适配器, 只重写需要 的方法就可以了