知识模块
一.进程和线程概述:
二.创建线程的第一种方式
三.Thread类的常用方法
四.创建线程的第二种方式
五.第三种创建方式
六.多线程安全问题
一.进程和线程概述:
进程:操作系统中运行的程序对应一个或多个进程
线程:一个进程中至少有一个线程,线程控制着进程的执行
二.创建线程的第一种方式
//线程定义
class 类名 extends Thread{
public void run(){
//定义线程要执行的程序
}
}
/* 创建线程并启动*/
创建该类对象然后调用start()方法
/*
自定义线程
*/
public class Thread01 extends Thread {
public void run() {
//线程要执行的任务
for (int i = 0; i < 10; i++) {
System.out.println("run..."+i);
}
}
}
/*
自定义线程
参照自定义异常思想
class AgeException extends Exception/RuntimeException{
}
class Dog extends Animal{
}
同理可得
class 类名 extends ???{
}
多线程运行特点:
CPU在多个线程之间做着随机切换执行操作,无论如何切换,最终一定会把每个线程
任务执行完
*/
public class ThreadDemo01 {
public static void main(String[] args) {
Thread01 t1 = new Thread01();
//t1.run();t1.run()仅仅相当于通过对象调用方法,不会去创建一个线程
t1.start();//创建一个线程并启动
for (int i = 0; i < 10; i++) {
System.out.println("main..." + i);
}
}
}
三.Thread类的常用方法
Thread类中的方法
static Thread currentThread()
获取当前执行的线程对象
String getName()
底层再给线程起名的时候默认是从0开始
Thread-0,Thread-1....
static void sleep(long millis)
可以让指定的线程休眠指定的毫秒值,当指定的毫秒值时间一到,该线程会自动醒来
当CPU再次切换到该线程的时候,会继续执行
/*
自定义线程
缺点:
class A extends B,Thread{//Java不支持多继承
}
*/
public class Thread01 extends Thread {
public void run() {
//线程要执行的任务
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println(getName()+"..."+i);
}
}
}
/*
Thread类中的方法
static Thread currentThread()
获取当前执行的线程对象
String getName()
底层再给线程起名的时候默认是从0开始
Thread-0,Thread-1....
static void sleep(long millis)
可以让指定的线程休眠指定的毫秒值,当指定的毫秒值时间一到,该线程会自动醒来
当CPU再次切换到该线程的时候,会继续执行
*/
public class ThreadDemo01 {
public static void main(String[] args) {
Thread01 t1 = new Thread01();
//t1.run();t1.run()仅仅相当于通过对象调用方法,不会去创建一个线程
t1.start();//创建一个线程并启动
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"..." + i);
}
}
}
四.创建线程的第二种方式
class 类名 implemetns Runnable{
public void run(){
//定义线程要执行的任务
}
}
/* 创建该线程并启动*/
new Thread(new 类名()).start();
public class Thread01 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " ... " + i);
}
}
}
/*
Thread类的构造方法
Thread(Runnable target)
分配新的 Thread 对象。
*/
public class ThreadDemo01 {
public static void main(String[] args) {
Thread01 t1 = new Thread01();
new Thread(t1).start();//new Thread()创建线程对象
//t1实现了Runnable接口,重写了run()方法,传给Thread()构造方法,相当于将线程要执行的任务传递给该线程
//最后调用start()方法会在底层创建并启动该线程
new Thread(t1).start();//又创建一个新的线程,执行的还是一样的任务
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "... " + i);
}
}
}
五.第三种创建方式
线程第三种创建方式:匿名内部类
格式:
new 父类/父接口(){
//重写父类或父接口的方法
};
原理:
父类
class 类名 extends 父类{
//重写父类的方法
}
new 类名();
父接口
class 类名 implement 父接口{
//实现父接口方法
}
new 类名();
实现代码1:
new Thread() {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + "..." + i);
}
}
}.start();
实现代码2:
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}).start();
/*
线程第三种创建方式:匿名内部类
格式:
new 父类/父接口(){
//重写父类或父接口的方法
};
原理:
父类
class 类名 extends 父类{
//重写父类的方法
}
new 类名();
父接口
class 类名 implement 父接口{
//实现父接口方法
}
new 类名();
*/
public class ThreadDemo01 {
public static void main(String[] args) {
//method01();
/*
class 类名 implemetns Runnable{
public void run(){
//定义线程要执行的任务
}
}
new Thread(new 类名()).start();
*/
/* Runnable r = new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
};
new Thread(r).start();*/
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}).start();
/* new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}).start();*/
}
private static void method01() {
/* //线程定义
class 类名 extends Thread{
public void run(){
//定义线程要执行的程序
}
}*/
/*Thread t = new Thread() {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + "..." + i);
}
}
};
t.start();*/
new Thread() {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + "..." + i);
}
}
}.start();
}
}
六.多线程安全问题
a.产生原因
由于多个线程操作同一份共享资源,其中一个线程还没有执行完操作共享数据的代码,CPU切换到
其他线程去执行,最终导致共享数据报错误
public class Ticket implements Runnable {
private int ticket = 100;
@Override
public void run() {
//卖票
while (true) {
if (ticket>0) {
/* try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
// System.out.println(Thread.currentThread().getName()+"..."+ticket--);
System.out.println(Thread.currentThread().getName()+"..."+ticket);
ticket--;
}
}
}
}
/*
多线程卖票:
三个窗口去卖100张票
A窗口 B窗口 C窗口
100 98 95
99 97 94
96
.. .. ..
我们考虑用线程去模拟窗口,线程执行任务为卖票(输出语句)
卖票案例的第一个问题:
1.卖出0票和-1票
2.卖出重复票
*/
public class ThreadDemo01 {
public static void main(String[] args) {
Ticket t = new Ticket();
new Thread(t,"A窗口").start();//模拟窗口A
new Thread(t,"B窗口").start();//模拟窗口B
new Thread(t,"C窗口").start();//模拟窗口C
}
}
b.解决方案
1.同步代码块:保证其中一个线程在操作共享数据的时候不被其他线程打扰
保证多线程操作共享资源的安全性
使用同步代码块保证多线程安全性
格式:
synchronized(锁对象){
我们一般将操作共享资源的代码梵高同步代码块中
}
锁对象指的是new出来的任意对象
1.如果某个线程进入同步代码块,该线程会持有该锁对象,其他线程就无法在进入同步代码块同时也无法持有该锁对象
2.如果某个线程走出同步代码块,该线程会释放该锁对象,此时其他某一个线程可以进入同步代码块同时可以持有该锁对象
注意:
所有的线程享有同一个锁对象才能保多线程操作共享资源安全性
public class Ticket implements Runnable {
private int ticket = 100;
@Override
public synchronized void run() {
//卖票
while (true) {
if (ticket>0) {
System.out.println(Thread.currentThread().getName()+"..."+ticket--);
}
}
}
}
/*
保证多线程操作共享资源的安全性
使用同步代码块保证多线程安全性
格式:
synchronized(锁对象){
我们一般将操作共享资源的代码梵高同步代码块中
}
锁对象指的是new出来的任意对象
1.如果某个线程进入同步代码块,该线程会持有该锁对象,其他线程就无法在进入同步代码块同时也无法持有该锁对象
2.如果某个线程走出同步代码块,该线程会释放该锁对象,此时其他某一个线程可以进入同步代码块同时可以持有该锁对象
注意:
所有的线程享有同一个锁对象才能保多线程操作共享资源安全性
*/
public class ThreadDemo01 {
public static void main(String[] args) {
Ticket t = new Ticket();
new Thread(t,"A窗口").start();//模拟窗口A
new Thread(t,"B窗口").start();//模拟窗口B
new Thread(t,"C窗口").start();//模拟窗口C
}
}
2.同步方法:
同步方法解决多线程安全问题:
格式:
修饰符 synchronized 返回值类型 方法名(参数列表){
}
同步方法相当于:
synchronized(this){//this引用指向new Thread()中传入的对象
//对方法中所有代码同步
}
我们一般将涉及到操作共享数据的方法单独抽取到一个方法中,然后在这个方法上加synchronized
/*
同步方法解决多线程安全问题:
格式:
修饰符 synchronized 返回值类型 方法名(参数列表){
}
同步方法相当于:
synchronized(this){//this引用指向new Thread()中传入的对象
//对方法中所有代码同步
}
我们一般将涉及到操作共享数据的方法单独抽取到一个方法中,然后在这个方法上加synchronized
*/
public class ThreadDemo01 {
public static void main(String[] args) {
Ticket t = new Ticket();
new Thread(t,"A窗口").start();//模拟窗口A
new Thread(t,"B窗口").start();//模拟窗口B
new Thread(t,"C窗口").start();//模拟窗口C
}
}
public class Ticket implements Runnable {
private int ticket = 100;
/*@Override
public synchronized void run() {
//卖票
while (true) {
if (ticket>0) {
System.out.println(Thread.currentThread().getName()+"..."+ticket--);
}
}
}*/
/* @Override
public void run() {
synchronized (this) {
System.out.println(this);
//卖票
while (true) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "..." + ticket--);
}
}
}
}*/
public void run() {
while (true) {
sellTicket();
}
}
private synchronized void sellTicket() {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "..." + ticket--);
}
}
}