标签:stack 文件读写 balance ticket tac dna java extends 系统
//单线程
public class Simple {
public static void main(String[] args) {
method2("atguigu.com");
}
public static void method2(String str) {
System.out.println("method2...");
method1(str);
}
public static void method1(String str) {
System.out.println("method1...");
System.out.println(str);
}
}
----------运行结果-----------
method2...
method1...
atguigu.com
/**
* 创建一个子线程,完成1-100之间自然数的输出,同样的,主线程完成同样的操作
* 创建多线程的第一种方式:继承java.lang.Thread类
*/
//1.创建一个继承Thread的子类
class SubThread extends Thread {
//2.重写Thread类中的run()方法,方法内实现子线程要完成的功能
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class TestThread {
public static void main(String[] args) {
//3.创建一个子类的对象
SubThread subThread = new SubThread();
//4.调用线程的start()方法:启动此线程,调用相应的run()方法
//一个线程只能够执行一次start()
//不能通过Thread实现类的run()方法去启动线程
subThread.start();
for (int i = 0; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
运行效果:(两个线程交叉运行)
创建两个子线程,一个输出1-100之间的偶数,另一个输出1-100之间的奇数
class SubThread1 extends Thread {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0)
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
class SubThread2 extends Thread {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
if (i % 2 == 1)
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class ThreadExer {
public static void main(String[] args) {
SubThread1 subThread1 = new SubThread1();
SubThread2 subThread2 = new SubThread2();
subThread1.start();
subThread2.start();
}
}
模拟火车站售票窗口
//模拟火车站售票窗口,开启三个售票窗口,总票数为20张
//存在线程的安全问题
class Window extends Thread {
private static int ticket = 20;
@Override
public void run() {
while (true) {
if (ticket > 0)
System.out.println(Thread.currentThread().getName() +
"售票,票号为:" + ticket-- + "号");
else
break;
}
}
}
public class TestWindow {
public static void main(String[] args) {
Window w1 = new Window();
Window w2 = new Window();
Window w3 = new Window();
w1.setName("窗口1");
w2.setName("窗口2");
w3.setName("窗口3");
w1.start();
w2.start();
w3.start();
}
}
------------运行结果---------------
窗口1售票,票号为:20号
窗口3售票,票号为:18号
窗口2售票,票号为:19号
窗口3售票,票号为:16号
窗口1售票,票号为:17号
窗口3售票,票号为:14号
窗口3售票,票号为:12号
窗口2售票,票号为:15号
窗口3售票,票号为:11号
窗口1售票,票号为:13号
窗口3售票,票号为:9号
窗口2售票,票号为:10号
窗口3售票,票号为:7号
窗口1售票,票号为:8号
窗口3售票,票号为:5号
窗口2售票,票号为:6号
窗口3售票,票号为:3号
窗口1售票,票号为:4号
窗口3售票,票号为:1号
窗口2售票,票号为:2号
创建两个子线程,一个输出1-100之间的偶数,另一个输出1-100之间的奇数
//创建多线程的方式二:通过实现的方法
//1.创建一个实现Runnable接口的类
class PrintNum1 implements Runnable {
//实现接口的抽象方法
@Override
public void run() {
for (int i = 1; i <= 100; i++)
if (i % 2 == 1)
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
class PrintNum2 implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 100; i++)
if (i % 2 == 0)
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
public class TestRunnable {
public static void main(String[] args) {
//3.创建一个Runnable接口实现的对象
PrintNum1 printNum1 = new PrintNum1();
PrintNum2 printNum2 = new PrintNum2();
//要启动多线程,必须使用start()方法
//4.将此对象最为形参传递给Thread类的构造器中,创建Thread类分对象,此对象即为一个线程
Thread thread1 = new Thread(printNum1);
//5.调用start()方法,启动线程并执行run()方法
thread1.start();
Thread thread2 = new Thread(printNum2);
thread2.start();
}
}
模拟火车站售票窗口
//使用Runnable接口的方法
//存在线程的安全问题,打印车票时,会出现重票、错票
class Window1 implements Runnable {
private int ticket = 20;
@Override
public void run() {
while (true) {
if (ticket > 0)
System.out.println(Thread.currentThread().getName() +
"售票,票号为:" + ticket-- + "号");
else
break;
}
}
}
public class RunnableWindow {
public static void main(String[] args) {
Window1 window1 = new Window1();
Thread thread1 = new Thread(window1);
Thread thread2 = new Thread(window1);
Thread thread3 = new Thread(window1);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread3.setName("窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
------------运行结果---------------
窗口1售票,票号为:20号
窗口1售票,票号为:17号
窗口1售票,票号为:16号
窗口3售票,票号为:18号
窗口3售票,票号为:14号
窗口2售票,票号为:19号
窗口3售票,票号为:13号
窗口1售票,票号为:15号
窗口3售票,票号为:11号
窗口2售票,票号为:12号
窗口3售票,票号为:9号
窗口1售票,票号为:10号
窗口3售票,票号为:7号
窗口2售票,票号为:8号
窗口3售票,票号为:5号
窗口1售票,票号为:6号
窗口3售票,票号为:3号
窗口2售票,票号为:4号
窗口3售票,票号为:1号
窗口1售票,票号为:2号
背景:只使用单个线程完成多个任务(调用多个方法),肯定比用多个线程来完成用的时间更短,为何仍需多线程呢?
多线程程序的优点:
Java中的线程分为两类:一种是==守护线程==,一种是==用户线程==。
JDK中用Thread.State枚举表示了线程的几种状态
多线程出现了安全问题
问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
解决方法
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
Java实现线程安全的方式
- 同步代码块
- 同步方法
线程同步的缺点
- 由于同一个时间只能有一个线程访问共享数据,效率变低了
同步代码块
synchronized (同步监视器){
// 需要被同步的代码;(即为操作共享数据的代码)
}
共享数据:多个线程共同操作的同一个数据(变量)
同步监视器:由一个类的对象来充当。那个线程获取此监视器,谁就执行大括号里被同步的代码。俗称:锁
要求:所有的线程必须使用同一把锁!
注:在实现的方式中,考虑同步的话,可以使用this来充当锁,但是在继承的方式中,慎用this
同步方法
public synchronized void show (String name){
····
}
模拟火车站售票窗口,并解决线程安全问题
继承方式使用同步代码块
static int ticket = 100;
static Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
if (ticket > 0) {
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println(Thread.currentThread().getName() +
"售票,票号为:" + ticket-- + "号");
} else
break;
}
}
}
实现方式使用同步代码块
private int ticket = 100;
@Override
public void run() {
while (true) {
synchronized (this){
if (ticket > 0) {
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println(Thread.currentThread().getName() +
"售票,票号为:" + ticket-- + "号");
}else
break;
}
}
}
实现方式使用同步方法
private int ticket = 100;
@Override
public void run() {
while (true) {
show();
}
}
public synchronized void show(){//同步方法
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() +
"售票,票号为:" + ticket-- + "号");
}
}
在Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
解决单例模式中懒汉式的问题
//关于懒汉式的线程安全问题:使用同步机制
//对于一般的方法,使用同步代码块,可以考虑使用this
//对于静态方法而言,使用当前类本身充当锁
class Singleton {
private Singleton() {
}
private static Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
例题:银行有一个账户。有两个储户分别向同一个账户存3000元,每次存1000,存3次。每次存完打印账户余额。
/**
* 1.是否涉及到多线程? 是,有两个储户
* 2.是否有共享数据? 有,同一个储户
* 3.考虑线程的同步
*/
class Account {
double balance; //余额
public Account() {
}
//存钱
public synchronized void deposit(double amt) {
balance += amt;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + balance);
}
}
class Customer extends Thread {
Account account;
static Object object = new Object();
public Customer(Account account) {
this.account = account;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
account.deposit(1000);
}
}
}
public class TestAccount {
public static void main(String[] args) {
Account acc = new Account();
Customer c1 = new Customer(acc);
Customer c2 = new Customer(acc);
c1.setName("甲");
c2.setName("乙");
c1.start();
c2.start();
}
}
------运行结果-------
甲:1000.0
乙:2000.0
乙:3000.0
乙:4000.0
甲:5000.0
甲:6000.0
线程的死锁问题
public class TestDeadLock {
static StringBuffer sb1 = new StringBuffer();
static StringBuffer sb2 = new StringBuffer();
public static void main(String[] args) {
new Thread() {
@Override
public void run() {
synchronized (sb1) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
sb1.append("A");
synchronized (sb2) {
sb2.append("B");
System.out.println(sb1);
System.out.println(sb2);
}
}
}
}.start();
new Thread() {
@Override
public void run() {
synchronized (sb2) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
sb1.append("C");
synchronized (sb1) {
sb2.append("D");
System.out.println(sb1);
System.out.println(sb2);
}
}
}
}.start();
}
}
class A {
public synchronized void foo(B b) {
System.out.println("当前线程名: " + Thread.currentThread().getName()
+ " 进入了A实例的foo方法"); // ①
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("当前线程名: " + Thread.currentThread().getName()
+ " 企图调用B实例的last方法"); // ③
b.last();
}
public synchronized void last() {
System.out.println("进入了A类的last方法内部");
}
}
class B {
public synchronized void bar(A a) {
System.out.println("当前线程名: " + Thread.currentThread().getName()
+ " 进入了B实例的bar方法"); // ②
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("当前线程名: " + Thread.currentThread().getName()
+ " 企图调用A实例的last方法"); // ④
a.last();
}
public synchronized void last() {
System.out.println("进入了B类的last方法内部");
}
}
public class DeadLock implements Runnable {
A a = new A();
B b = new B();
public void init() {
Thread.currentThread().setName("主线程");
// 调用a对象的foo方法
a.foo(b);
System.out.println("进入了主线程之后");
}
public void run() {
Thread.currentThread().setName("副线程");
// 调用b对象的bar方法
b.bar(a);
System.out.println("进入了副线程之后");
}
public static void main(String[] args) {
DeadLock dl = new DeadLock();
new Thread(dl).start();
dl.init();
}
}
使用两个线程打印1-100,线程1,线程2交替打印
//线程通信:如下的三个关键字使用的话,都得在同步代码块或同步方法中
//wait():一旦一个线程执行到wait(),就释放当前的锁
//notify()/notifyAll():唤醒wait的一个或多个线程
//使用两个线程打印1-100,线程1,线程2交替打印
class PrintNum implements Runnable {
int num = 1;
@Override
public void run() {
while (true) {
synchronized (this) {
notify();
if (num <= 100) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + num);
num++;
} else
break;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class TestCommunication {
public static void main(String[] args) {
PrintNum printNum = new PrintNum();
Thread t1 = new Thread(printNum);
Thread t2 = new Thread(printNum);
t1.setName("甲");
t2.setName("乙");
t1.start();
t2.start();
}
}
/**
* 1.是否涉及到多线程的问题? 是,生产者、消费者
* 2.是否涉及到共享数据? 有,考虑数据的数量
* 3.共享数据:产品数量
* 4.是否涉及到线程的通信? 存在着生产者与消费者的通信
*/
class Clerk {
int product;
public synchronized void addProduct() { //生产产品
if (product >= 20){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
product++;
System.out.println(Thread.currentThread().getName() +
":生产了第" + product + "件产品");
notifyAll();
}
}
public synchronized void consumeProduct() { //消费产品
if (product <= 0){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
System.out.println(Thread.currentThread().getName() +
":消费了第" + product + "件产品");
product--;
notifyAll();
}
}
}
class Producer implements Runnable {
Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println("生产者开始生产产品");
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.addProduct();
}
}
}
class Consumer implements Runnable {
Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println("消费者消费产品");
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.consumeProduct();
}
}
}
public class ProduceConsume {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer1 = new Producer(clerk);
Producer producer2 = new Producer(clerk);
Consumer consumer1 = new Consumer(clerk);
Consumer consumer2 = new Consumer(clerk);
Thread pro1 = new Thread(producer1);
Thread pro2 = new Thread(producer2);
Thread con1 = new Thread(consumer1);
Thread con2 = new Thread(consumer2);
pro1.setName("生产者1");
pro2.setName("生产者2");
con1.setName("消费者1");
con2.setName("消费者2");
pro1.start();
pro2.start();
con1.start();
con2.start();
}
}
标签:stack 文件读写 balance ticket tac dna java extends 系统
原文地址:https://www.cnblogs.com/wffrzh/p/9484553.html