------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
兴趣是学习编程最好的老师
不积跬步,无以至千里
这辈子没办法做太多事情,所以每一件都要做到精彩绝伦。
1.线程是程序执行的一条路径, 一个进程中可以包含多条线程,多线程并发执行可以提高程序的效率, 可以同时完成多项工作。
2.开启新线程的两种方式
1).继承Thread
public static void main(String[] args) {
MyThread mt = new MyThread(); //4,创建自定义类对象
mt.start(); <span style="font-family:宋体;">//5,开启线程
</span> for(int i = 0; i < 10000; i++) {
System.out.println("传智播客");
}
}
}
class MyThread extends Thread { //1,自定义类继承Thread
public void run() { //2,重写run方法
for(int i = 0; i < 10000; i++) { //3,将要执行的代码写在run方法中
System.out.println("黑马程序员");
}
}
}
2).实现Runnable
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();//4,创建自定义类对象
Thread t = new Thread(mr); //5,将自定义类对象当作参数传递给Thread的构造函数
t.start(); //6,开启线程
for(int i = 0; i < 10000; i++) {
System.out.println("传智播客");
}
}
}
class MyRunnable implements Runnable { //1,自定义类实现Runnable接口
@Override
public void run() { //2,重写run方法
for(int i = 0; i < 10000; i++) {//3,将要执行的代码写在run方法中
System.out.println("黑马程序员");
}
}
}
3.Thread类常用方法
获取名字: getName()方法
设置名字:通过构造函数可以传入String类型的名字或通过setName(String)方法可以设置线程对象的名字
获取当前线程对象:Thread.currentThread()
休眠:Thread.sleep(毫秒,纳秒)
守护:setDaemon()
加入:
join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
join(int), 可以等待指定的毫秒之后继续
让出cpu:yield
设置线程的优先级:setPriority()
public static void main(String[] args) {
final Thread t1 = new Thread() {
public void run() {
for(int i = 0; i < 50; i++) {
try {
Thread.sleep(10);//休眠
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(getName() + "...传智播客");
}
}
};
Thread t2 = new Thread(new Runnable(){
public void run() {
for(int i = 0; i < 50; i++) {
if(i == 2) {
try {
//t1.join();//让出cpu,只有t1完全执行完,再执行t2
t1.join(1);//让出cpu,但是只让出1毫秒,就是指定参数的时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "...黑马程序员");
} //获取当前线程对象
}
});
t1.setDaemon(true); //设置t1为守护线程
t1.start();
t2.start();
}
4.线程之间的同步
线程之间的同步有同步代码块和同步方法
同步代码块是使用synchronized关键字加上一个锁对象来定义一段代码,多个同步代码块使用相同的锁对象
同步方法是使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的,非静态同步方法默认使用当 前对象this作为锁对象,静态方同步函数的锁是该类的字节码对象。字节码对象可以认为是万能锁。
要注意的是多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁,所以尽可能不要嵌套使用。
例如:模拟售票,总数为一千张,有四个窗口卖票:
public class Ticket {
public static void main(String[] args) {
new TicketsSeller().start(); //开启线程
new TicketsSeller().start();
new TicketsSeller().start();
new TicketsSeller().start();
}
}
class TicketsSeller extends Thread {
private static int tickets = 1000;
public void run() {
while(true) {
synchronized(TicketsSeller.class) {
if(tickets == 0)
break;
try {
Thread.sleep(10);//增强效果
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + "...这是第" + tickets-- + "号票");//卖票,并将结果--
}
}
}
}
5.单例设计模式
单例设计模式:保证类在内存中只有一个对象。
(1)将构造方法私有。
(2)在本类中定义一个本类的对象。Singleton s;
(3)提供公共的访问方式。public static Singleton getInstance(){return s}
单例写法两种:饿汉式,懒汉式;
饿汉式:
class Singleton{
private Singleton(){}
private static Singleton s=new Singleton();
public static Singleton getInstance(){
return s;
}
}
懒汉式:
class Singleton{
private Sinleton(){}
private static Singleton s;
public static Sinleton getInstance(){
if(s==null){
s=new Sinleton();}
return s;
}
}
第三种格式:
class Singleton {
private Singleton() {}
public static final Singleton s = new Singleton();//final是最终的意思,被final修饰的变量不可以被更改
}
6.线程之间的通信
线程等待, wait()
唤醒等待 notify();
这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用
多个线程通信的问题
notify()方法是随机唤醒一个线程
notifyAll()方法是唤醒所有线程
JDK5之前无法唤醒指定的一个线程
如果多个线程之间通信, 需要使用notifyAll()通知所有线程, 用while来反复判断条件
public class Demo6_Notify {
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; //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("员");
System.out.print("\r\n");
flag = 1; //flag改为1
this.notify(); //唤醒等待的线程
}
}
7.JDK5之后的线程控制
同步
使用ReentrantLock类的lock()和unlock()方法进行同步
通信
使用ReentrantLock类的newCondition()方法可以获取Condition对象
需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法
不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了
package cn.itcast.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Demo8_ReentrantLock{
public static void main(String[] args) {
final Printer3 p = new Printer3();
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 Printer3 {
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; //flag改为2
c2.signal(); //唤醒线程2
r.unlock(); //释放锁
}
public void print2() throws InterruptedException {
r.lock(); //获取锁
if(flag != 2) {
c2.await(); //线程等待,2线程等待
}
System.out.print("黑");
System.out.print("马");
System.out.print("程");
System.out.print("序");
System.out.print("员");
System.out.print("\r\n");
flag = 3; //flag改为3
c3.signal(); //唤醒线程3
r.unlock(); //释放锁
}
public void print3() throws InterruptedException {
r.lock(); //获取锁
if(flag != 3) {
c3.await(); //线程等待,3线程等待
}
System.out.print("i");
System.out.print("t");
System.out.print("c");
System.out.print("a");
System.out.print("s");
System.out.print("t");
System.out.print("\r\n");
flag = 1; //flag改为1
c1.signal(); //唤醒线程1
r.unlock(); //释放锁
}
}